TraceLinearScanLifetimeAnalysisPhase.java revision 13264:48566d838608
1139825Simp/*
21541Srgrimes * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
31541Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41541Srgrimes *
51541Srgrimes * This code is free software; you can redistribute it and/or modify it
61541Srgrimes * under the terms of the GNU General Public License version 2 only, as
71541Srgrimes * published by the Free Software Foundation.
81541Srgrimes *
91541Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
101541Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
111541Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
121541Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
131541Srgrimes * accompanied this code).
141541Srgrimes *
151541Srgrimes * You should have received a copy of the GNU General Public License version
161541Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
171541Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
181541Srgrimes *
191541Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
201541Srgrimes * or visit www.oracle.com if you need additional information or have any
211541Srgrimes * questions.
221541Srgrimes */
231541Srgrimespackage org.graalvm.compiler.lir.alloc.trace.lsra;
241541Srgrimes
251541Srgrimesimport static jdk.vm.ci.code.ValueUtil.asRegisterValue;
261541Srgrimesimport static jdk.vm.ci.code.ValueUtil.asStackSlot;
271541Srgrimesimport static jdk.vm.ci.code.ValueUtil.isRegister;
281541Srgrimesimport static jdk.vm.ci.code.ValueUtil.isStackSlot;
291541Srgrimesimport static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
301541Srgrimesimport static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
311541Srgrimesimport static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
32116226Sobrienimport static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation;
33116226Sobrienimport static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAuseInterTraceHints;
34116226Sobrienimport static org.graalvm.compiler.lir.alloc.trace.TraceUtil.asShadowedRegisterValue;
351541Srgrimesimport static org.graalvm.compiler.lir.alloc.trace.TraceUtil.isShadowedRegisterValue;
361541Srgrimesimport static org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.isVariableOrRegister;
371541Srgrimes
3876166Smarkmimport java.util.ArrayList;
3976166Smarkmimport java.util.EnumSet;
4076166Smarkm
4134924Sbdeimport org.graalvm.compiler.core.common.LIRKind;
4274927Sjhbimport org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
4312662Sdgimport org.graalvm.compiler.core.common.alloc.Trace;
4493823Sdillonimport org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
4512662Sdgimport org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
461541Srgrimesimport org.graalvm.compiler.debug.DebugContext;
4740794Speterimport org.graalvm.compiler.debug.GraalError;
4812726Sbdeimport org.graalvm.compiler.debug.Indent;
4912662Sdgimport org.graalvm.compiler.lir.InstructionValueConsumer;
5012662Sdgimport org.graalvm.compiler.lir.LIR;
5112662Sdgimport org.graalvm.compiler.lir.LIRInstruction;
5212662Sdgimport org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
531541Srgrimesimport org.graalvm.compiler.lir.LIRInstruction.OperandMode;
541541Srgrimesimport org.graalvm.compiler.lir.LIRValueUtil;
55170170Sattilioimport org.graalvm.compiler.lir.StandardOp.JumpOp;
561541Srgrimesimport org.graalvm.compiler.lir.StandardOp.LabelOp;
5776778Sjhbimport org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
581541Srgrimesimport org.graalvm.compiler.lir.StandardOp.ValueMoveOp;
5962622Sjhbimport org.graalvm.compiler.lir.ValueProcedure;
60170170Sattilioimport org.graalvm.compiler.lir.Variable;
6162622Sjhbimport org.graalvm.compiler.lir.alloc.trace.GlobalLivenessInfo;
62170170Sattilioimport org.graalvm.compiler.lir.alloc.trace.ShadowedRegisterValue;
6362622Sjhbimport org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterPriority;
64170170Sattilioimport org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.SpillState;
6562622Sjhbimport org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan;
66170170Sattilioimport org.graalvm.compiler.lir.gen.LIRGenerationResult;
6762622Sjhbimport org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
68170170Sattilioimport org.graalvm.compiler.lir.ssa.SSAUtil;
6962622Sjhb
70170170Sattilioimport jdk.vm.ci.code.Register;
7162622Sjhbimport jdk.vm.ci.code.RegisterArray;
72170170Sattilioimport jdk.vm.ci.code.RegisterValue;
7362622Sjhbimport jdk.vm.ci.code.TargetDescription;
74170170Sattilioimport jdk.vm.ci.meta.AllocatableValue;
7512286Sphkimport jdk.vm.ci.meta.JavaConstant;
76136404Speterimport jdk.vm.ci.meta.Value;
77136404Speter
78136404Speterpublic final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanAllocationPhase {
79136404Speter
80136404Speter    @Override
811541Srgrimes    protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig,
82136404Speter                    TraceBuilderResult traceBuilderResult, TraceLinearScan allocator) {
83136404Speter        new Analyser(allocator, traceBuilderResult).analyze();
84136404Speter    }
85136404Speter
86136404Speter    public static final class Analyser {
87136404Speter        private final TraceLinearScan allocator;
88136404Speter        private final DebugContext debug;
89136404Speter        private final TraceBuilderResult traceBuilderResult;
90136404Speter        private int numInstructions;
91136404Speter
92136404Speter        public Analyser(TraceLinearScan allocator, TraceBuilderResult traceBuilderResult) {
93136404Speter            this.allocator = allocator;
94136404Speter            this.debug = allocator.getDebug();
9512286Sphk            this.traceBuilderResult = traceBuilderResult;
9662573Sphk        }
971541Srgrimes
9899072Sjulian        private AbstractBlockBase<?>[] sortedBlocks() {
9912286Sphk            return allocator.sortedBlocks();
100164437Sru        }
10112286Sphk
10212286Sphk        private LIR getLIR() {
10312286Sphk            return allocator.getLIR();
1041541Srgrimes        }
10599072Sjulian
106159054Stegge        private RegisterArray getCallerSavedRegisters() {
1071541Srgrimes            return allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters();
108164437Sru        }
1091541Srgrimes
1101541Srgrimes        public void analyze() {
1111541Srgrimes            countInstructions();
11279224Sdillon            buildIntervals();
11395112Salc        }
114108551Salc
115124083Salc        private boolean isAllocated(AbstractBlockBase<?> currentBlock, AbstractBlockBase<?> other) {
116124083Salc            return traceBuilderResult.getTraceForBlock(other).getId() < traceBuilderResult.getTraceForBlock(currentBlock).getId();
117124083Salc        }
118124083Salc
119124083Salc        /**
120124083Salc         * Count instructions in all blocks. The numbering follows the
121124083Salc         * {@linkplain TraceLinearScan#sortedBlocks() register allocation order}.
122124083Salc         */
12338517Sdfr        private void countInstructions() {
124113448Salc
125108551Salc            allocator.initIntervals();
12695112Salc
1271541Srgrimes            int numberInstructions = 0;
1281541Srgrimes            for (AbstractBlockBase<?> block : sortedBlocks()) {
1291541Srgrimes                numberInstructions += getLIR().getLIRforBlock(block).size();
13074927Sjhb            }
13183366Sjulian            numInstructions = numberInstructions;
1321541Srgrimes
1331541Srgrimes            // initialize with correct length
134170307Sjeff            allocator.initOpIdMaps(numberInstructions);
13599072Sjulian        }
13699072Sjulian
137170307Sjeff        private final InstructionValueConsumer outputConsumer = new InstructionValueConsumer() {
1381541Srgrimes            @Override
1391541Srgrimes            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
14099072Sjulian                if (isVariableOrRegister(operand)) {
14199072Sjulian                    addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op));
142103216Sjulian                    addRegisterHint(op, operand, mode, flags, true);
143170307Sjeff                }
14499072Sjulian            }
145103216Sjulian        };
146170307Sjeff
147170307Sjeff        private final InstructionValueConsumer tempConsumer = new InstructionValueConsumer() {
148170307Sjeff            @Override
149104387Sjhb            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
150103216Sjulian                if (isVariableOrRegister(operand)) {
151103216Sjulian                    addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister);
152164437Sru                    addRegisterHint(op, operand, mode, flags, false);
153103216Sjulian                }
154103216Sjulian            }
155103216Sjulian        };
15699072Sjulian        private final InstructionValueConsumer aliveConsumer = new InstructionValueConsumer() {
157164437Sru            @Override
158103216Sjulian            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
159164437Sru                if (isVariableOrRegister(operand)) {
16099072Sjulian                    RegisterPriority p = registerPriorityOfInputOperand(flags);
16199072Sjulian                    int opId = op.id();
1621541Srgrimes                    int blockFrom = 0;
163103216Sjulian                    addUse((AllocatableValue) operand, blockFrom, opId + 1, p);
164164437Sru                    addRegisterHint(op, operand, mode, flags, false);
165103216Sjulian                }
16699072Sjulian            }
16799072Sjulian        };
168164437Sru
169170307Sjeff        private final InstructionValueConsumer inputConsumer = new InstructionValueConsumer() {
17099072Sjulian            @Override
17199072Sjulian            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
17299072Sjulian                if (isVariableOrRegister(operand)) {
17399072Sjulian                    int opId = op.id();
174170307Sjeff                    RegisterPriority p = registerPriorityOfInputOperand(flags);
17571572Sjhb                    int blockFrom = 0;
1761541Srgrimes                    addUse((AllocatableValue) operand, blockFrom, opId, p);
177170307Sjeff                    addRegisterHint(op, operand, mode, flags, false);
1781541Srgrimes                }
1791541Srgrimes            }
1801541Srgrimes
1811541Srgrimes        };
182159054Stegge
183159054Stegge        private final InstructionValueConsumer stateProc = new InstructionValueConsumer() {
184159054Stegge            @Override
185159054Stegge            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
186108585Salc                if (isVariableOrRegister(operand)) {
187108585Salc                    int opId = op.id();
1885455Sdg                    int blockFrom = 0;
18943748Sdillon                    addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None);
190108585Salc                }
1911541Srgrimes            }
192113448Salc        };
193108585Salc
194108585Salc        private void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority) {
195113448Salc            if (isRegister(operand)) {
1961541Srgrimes                RegisterValue reg = asRegisterValue(operand);
197108585Salc                if (allocator.isAllocatable(reg)) {
198159054Stegge                    addFixedUse(reg, from, to);
1991541Srgrimes                }
200164437Sru            } else {
2011541Srgrimes                assert isVariable(operand) : operand;
20274927Sjhb                addVariableUse(asVariable(operand), from, to, registerPriority);
2031541Srgrimes            }
2041541Srgrimes        }
2051541Srgrimes
20695112Salc        private void addFixedUse(RegisterValue reg, int from, int to) {
20775523Salfred            FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
20842957Sdillon            interval.addRange(from, to);
209124083Salc            if (debug.isLogEnabled()) {
210124083Salc                debug.log("add fixed use: %s, at %d", interval, to);
211124083Salc            }
212124083Salc        }
21342957Sdillon
214108585Salc        private void addVariableUse(Variable operand, int from, int to, RegisterPriority registerPriority) {
215124083Salc            TraceInterval interval = allocator.getOrCreateInterval(operand);
216124083Salc            interval.addRange(from, to);
217124083Salc
21842957Sdillon            // Register use position at even instruction id.
219108585Salc            interval.addUsePos(to & ~1, registerPriority, allocator.getOptions());
220164429Sru
221164429Sru            if (debug.isLogEnabled()) {
222164429Sru                debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name());
223164429Sru            }
224164429Sru        }
225164429Sru
226164429Sru        private void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority) {
227164437Sru            if (isRegister(operand)) {
228164437Sru                RegisterValue reg = asRegisterValue(operand);
2291541Srgrimes                if (allocator.isAllocatable(reg)) {
230164437Sru                    addFixedDef(reg, op);
231164437Sru                }
2321541Srgrimes            } else {
23318169Sdyson                assert isVariable(operand) : operand;
2341541Srgrimes                addVariableDef(asVariable(operand), op, registerPriority);
235164437Sru            }
236164437Sru        }
2371541Srgrimes
238164437Sru        private void addFixedDef(RegisterValue reg, LIRInstruction op) {
239164437Sru            FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
2401541Srgrimes            int defPos = op.id();
2411541Srgrimes            if (interval.from() <= defPos) {
2421541Srgrimes                /*
24395112Salc                 * Update the starting point (when a range is first created for a use, its start is
244170170Sattilio                 * the beginning of the current block until a def is encountered).
245164437Sru                 */
2461541Srgrimes                interval.setFrom(defPos);
24712286Sphk
24893823Sdillon            } else {
24993823Sdillon                /*
25093823Sdillon                 * Dead value - make vacuous interval also add register priority for dead intervals
25193823Sdillon                 */
25293823Sdillon                interval.addRange(defPos, defPos + 1);
25393823Sdillon                if (debug.isLogEnabled()) {
25493823Sdillon                    debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos);
25593823Sdillon                }
25693823Sdillon            }
25793823Sdillon            if (debug.isLogEnabled()) {
25893823Sdillon                debug.log("add fixed def: %s, at %d", interval, defPos);
25993823Sdillon            }
26093823Sdillon        }
26193823Sdillon
26293823Sdillon        private TraceInterval addVariableDef(Variable operand, LIRInstruction op, RegisterPriority registerPriority) {
26393823Sdillon            int defPos = op.id();
264170170Sattilio
26593823Sdillon            TraceInterval interval = allocator.getOrCreateInterval(operand);
26693823Sdillon
26793823Sdillon            if (interval.isEmpty()) {
26893823Sdillon                /*
26993823Sdillon                 * Dead value - make vacuous interval also add register priority for dead intervals
27093823Sdillon                 */
27193823Sdillon                interval.addRange(defPos, defPos + 1);
272108171Sdillon                if (debug.isLogEnabled()) {
273108171Sdillon                    debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
27493823Sdillon                }
275144970Sjhb            } else {
27693823Sdillon                /*
27793823Sdillon                 * Update the starting point (when a range is first created for a use, its start is
278109097Sdillon                 * the beginning of the current block until a def is encountered).
27946381Sbillf                 */
28046381Sbillf                interval.setFrom(defPos);
281141696Sphk            }
282141630Sphk            if (!(op instanceof LabelOp)) {
283141630Sphk                // no use positions for labels
284141630Sphk                interval.addUsePos(defPos, registerPriority, allocator.getOptions());
285141630Sphk            }
28640794Speter
28793823Sdillon            changeSpillDefinitionPos(op, operand, interval, defPos);
28893823Sdillon            if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) {
289170170Sattilio                // detection of method-parameters and roundfp-results
29093823Sdillon                interval.setSpillState(SpillState.StartInMemory);
291170170Sattilio            }
29293823Sdillon            interval.addMaterializationValue(getMaterializedValue(op, operand, interval, allocator.neverSpillConstants(), allocator.getSpillMoveFactory()));
293170170Sattilio
29493823Sdillon            if (debug.isLogEnabled()) {
295170170Sattilio                debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
29693823Sdillon            }
297170170Sattilio            return interval;
29893823Sdillon        }
299170170Sattilio
30093823Sdillon        private void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority) {
301170170Sattilio            if (isRegister(operand)) {
30293823Sdillon                RegisterValue reg = asRegisterValue(operand);
303170170Sattilio                if (allocator.isAllocatable(reg)) {
30493823Sdillon                    addFixedTemp(reg, tempPos);
305170170Sattilio                }
30693823Sdillon            } else {
307170170Sattilio                assert isVariable(operand) : operand;
30893823Sdillon                addVariableTemp(asVariable(operand), tempPos, registerPriority);
309170170Sattilio            }
31093823Sdillon        }
311170170Sattilio
31293823Sdillon        private void addFixedTemp(RegisterValue reg, int tempPos) {
313170170Sattilio            FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
31493823Sdillon            interval.addRange(tempPos, tempPos + 1);
315170170Sattilio            if (debug.isLogEnabled()) {
31693823Sdillon                debug.log("add fixed temp: %s, at %d", interval, tempPos);
317170170Sattilio            }
31893823Sdillon        }
319170170Sattilio
32093823Sdillon        private void addVariableTemp(Variable operand, int tempPos, RegisterPriority registerPriority) {
321170170Sattilio            TraceInterval interval = allocator.getOrCreateInterval(operand);
32293823Sdillon
323170170Sattilio            if (interval.isEmpty()) {
32493823Sdillon                interval.addRange(tempPos, tempPos + 1);
325170170Sattilio            } else if (interval.from() > tempPos) {
32693823Sdillon                interval.setFrom(tempPos);
327170170Sattilio            }
32893823Sdillon
329170170Sattilio            interval.addUsePos(tempPos, registerPriority, allocator.getOptions());
33093823Sdillon            interval.addMaterializationValue(null);
331170170Sattilio
332171633Salc            if (debug.isLogEnabled()) {
333171633Salc                debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
33493823Sdillon            }
335170170Sattilio        }
33693823Sdillon
337170170Sattilio        /**
33893823Sdillon         * Eliminates moves from register to stack if the stack slot is known to be correct.
339170170Sattilio         *
34093823Sdillon         * @param op
341170170Sattilio         * @param operand
34293823Sdillon         */
343170170Sattilio        private void changeSpillDefinitionPos(LIRInstruction op, AllocatableValue operand, TraceInterval interval, int defPos) {
34493823Sdillon            assert interval.isSplitParent() : "can only be called for split parents";
345170170Sattilio
34693823Sdillon            switch (interval.spillState()) {
347170170Sattilio                case NoDefinitionFound:
34893823Sdillon                    // assert interval.spillDefinitionPos() == -1 : "must no be set before";
349170170Sattilio                    interval.setSpillDefinitionPos(defPos);
35093823Sdillon                    if (!(op instanceof LabelOp)) {
351170170Sattilio                        // Do not update state for labels. This will be done afterwards.
35293823Sdillon                        interval.setSpillState(SpillState.NoSpillStore);
353170170Sattilio                    }
35493823Sdillon                    break;
355170170Sattilio
35693823Sdillon                case NoSpillStore:
357170170Sattilio                    assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created";
35893823Sdillon                    if (defPos < interval.spillDefinitionPos() - 2) {
359170170Sattilio                        /*
36093823Sdillon                         * Second definition found, so no spill optimization possible for this
361170170Sattilio                         * interval.
36293823Sdillon                         */
363170170Sattilio                        interval.setSpillState(SpillState.NoOptimization);
36493823Sdillon                    } else {
365170170Sattilio                        // two consecutive definitions (because of two-operand LIR form)
36693823Sdillon                        assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal";
367170170Sattilio                    }
368170170Sattilio                    break;
369170170Sattilio
37093823Sdillon                case NoOptimization:
371170170Sattilio                    // nothing to do
37293823Sdillon                    break;
373170170Sattilio
37493823Sdillon                default:
375170170Sattilio                    throw GraalError.shouldNotReachHere("other states not allowed at this time");
37693823Sdillon            }
377170170Sattilio        }
37893823Sdillon
379170170Sattilio        private void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
38093823Sdillon            if (flags.contains(OperandFlag.HINT) && isVariableOrRegister(targetValue)) {
381170170Sattilio
38293823Sdillon                ValueProcedure registerHintProc = new ValueProcedure() {
383170170Sattilio                    @Override
38493823Sdillon                    public Value doValue(Value registerHint, OperandMode valueMode, EnumSet<OperandFlag> valueFlags) {
385170170Sattilio                        if (isVariableOrRegister(registerHint)) {
38693823Sdillon                            /*
38740794Speter                             * TODO (je): clean up
38840794Speter                             */
389                            final AllocatableValue fromValue;
390                            final AllocatableValue toValue;
391                            /* hints always point from def to use */
392                            if (hintAtDef) {
393                                fromValue = (AllocatableValue) registerHint;
394                                toValue = (AllocatableValue) targetValue;
395                            } else {
396                                fromValue = (AllocatableValue) targetValue;
397                                toValue = (AllocatableValue) registerHint;
398                            }
399                            debug.log("addRegisterHint %s to %s", fromValue, toValue);
400                            final TraceInterval to;
401                            final IntervalHint from;
402                            if (isRegister(toValue)) {
403                                if (isRegister(fromValue)) {
404                                    // fixed to fixed move
405                                    return null;
406                                }
407                                from = getIntervalHint(toValue);
408                                to = allocator.getOrCreateInterval(asVariable(fromValue));
409                            } else {
410                                to = allocator.getOrCreateInterval(asVariable(toValue));
411                                from = getIntervalHint(fromValue);
412                            }
413
414                            to.setLocationHint(from);
415                            if (debug.isLogEnabled()) {
416                                debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
417                            }
418
419                            return registerHint;
420                        }
421                        return null;
422                    }
423                };
424                op.forEachRegisterHint(targetValue, mode, registerHintProc);
425            }
426        }
427
428        private static boolean optimizeMethodArgument(Value value) {
429            /*
430             * Object method arguments that are passed on the stack are currently not optimized
431             * because this requires that the runtime visits method arguments during stack walking.
432             */
433            return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && LIRKind.isValue(value);
434        }
435
436        /**
437         * Determines the register priority for an instruction's output/result operand.
438         */
439        private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) {
440            if (op instanceof LabelOp) {
441                // skip method header
442                return RegisterPriority.None;
443            }
444            if (ValueMoveOp.isValueMoveOp(op)) {
445                ValueMoveOp move = ValueMoveOp.asValueMoveOp(op);
446                if (optimizeMethodArgument(move.getInput())) {
447                    return RegisterPriority.None;
448                }
449            }
450
451            // all other operands require a register
452            return RegisterPriority.MustHaveRegister;
453        }
454
455        /**
456         * Determines the priority which with an instruction's input operand will be allocated a
457         * register.
458         */
459        private static RegisterPriority registerPriorityOfInputOperand(EnumSet<OperandFlag> flags) {
460            if (flags.contains(OperandFlag.OUTGOING)) {
461                return RegisterPriority.None;
462            }
463            if (flags.contains(OperandFlag.STACK)) {
464                return RegisterPriority.ShouldHaveRegister;
465            }
466            // all other operands require a register
467            return RegisterPriority.MustHaveRegister;
468        }
469
470        @SuppressWarnings("try")
471        private void buildIntervals() {
472
473            try (Indent indent = debug.logAndIndent("build intervals")) {
474
475                // create a list with all caller-save registers (cpu, fpu, xmm)
476                RegisterArray callerSaveRegs = getCallerSavedRegisters();
477                int instructionIndex = numInstructions;
478
479                // iterate all blocks in reverse order
480                AbstractBlockBase<?>[] blocks = sortedBlocks();
481                for (int blockId = blocks.length - 1; blockId >= 0; blockId--) {
482                    final AbstractBlockBase<?> block = blocks[blockId];
483
484                    try (Indent indent2 = debug.logAndIndent("handle block %d", block.getId())) {
485                        handleBlockEnd(block, (instructionIndex - 1) << 1);
486
487                        /*
488                         * Iterate all instructions of the block in reverse order. definitions of
489                         * intervals are processed before uses.
490                         */
491                        ArrayList<LIRInstruction> instructions = getLIR().getLIRforBlock(block);
492                        for (int instIdx = instructions.size() - 1; instIdx >= 1; instIdx--) {
493                            final LIRInstruction op = instructions.get(instIdx);
494                            // number instruction
495                            instructionIndex--;
496                            numberInstruction(block, op, instructionIndex);
497                            final int opId = op.id();
498
499                            try (Indent indent3 = debug.logAndIndent("handle inst %d: %s", opId, op)) {
500
501                                /*
502                                 * Add a temp range for each register if operation destroys
503                                 * caller-save registers.
504                                 */
505                                if (op.destroysCallerSavedRegisters()) {
506                                    for (Register r : callerSaveRegs) {
507                                        if (allocator.attributes(r).isAllocatable()) {
508                                            addTemp(r.asValue(), opId, RegisterPriority.None);
509                                        }
510                                    }
511                                    if (debug.isLogEnabled()) {
512                                        debug.log("operation destroys all caller-save registers");
513                                    }
514                                }
515
516                                op.visitEachOutput(outputConsumer);
517                                op.visitEachTemp(tempConsumer);
518                                op.visitEachAlive(aliveConsumer);
519                                op.visitEachInput(inputConsumer);
520
521                                /*
522                                 * Add uses of live locals from interpreter's point of view for
523                                 * proper debug information generation. Treat these operands as temp
524                                 * values (if the live range is extended to a call site, the value
525                                 * would be in a register at the call otherwise).
526                                 */
527                                op.visitEachState(stateProc);
528                            }
529
530                        }   // end of instruction iteration
531                            // number label instruction
532                        instructionIndex--;
533                        numberInstruction(block, instructions.get(0), instructionIndex);
534                        AbstractBlockBase<?> pred = blockId == 0 ? null : blocks[blockId - 1];
535                        handleBlockBegin(block, pred);
536                    }
537                    if (debug.isDumpEnabled(DebugContext.VERY_DETAILED_LEVEL)) {
538                        allocator.printIntervals("After Block " + block);
539                    }
540                }   // end of block iteration
541                assert instructionIndex == 0 : "not at start?" + instructionIndex;
542                handleTraceBegin(blocks[0]);
543
544                // fix spill state for phi/incoming intervals
545                for (TraceInterval interval : allocator.intervals()) {
546                    if (interval != null && interval.spillState().equals(SpillState.NoDefinitionFound) && interval.spillDefinitionPos() != -1) {
547                        // there was a definition in a phi/incoming
548                        interval.setSpillState(SpillState.NoSpillStore);
549                    }
550                }
551                if (TraceRAuseInterTraceHints.getValue(allocator.getLIR().getOptions())) {
552                    addInterTraceHints();
553                }
554                for (FixedInterval interval1 : allocator.fixedIntervals()) {
555                    if (interval1 != null) {
556                        /* We use [-1, 0] to avoid intersection with incoming values. */
557                        interval1.addRange(-1, 0);
558                    }
559                }
560            }
561        }
562
563        private void handleTraceBegin(AbstractBlockBase<?> block) {
564            LIRInstruction op = getLIR().getLIRforBlock(block).get(0);
565            GlobalLivenessInfo livenessInfo = allocator.getGlobalLivenessInfo();
566            for (int varNum : livenessInfo.getBlockIn(block)) {
567                if (isAliveAtBlockBegin(varNum)) {
568                    addVariableDef(livenessInfo.getVariable(varNum), op, RegisterPriority.None);
569                }
570            }
571        }
572
573        private boolean isAliveAtBlockBegin(int varNum) {
574            return allocator.intervalFor(varNum) != null;
575        }
576
577        private void handleBlockBegin(AbstractBlockBase<?> block, AbstractBlockBase<?> pred) {
578            if (SSAUtil.isMerge(block)) {
579                // handle phis
580                // method parameters are fixed later on (see end of #buildIntervals)
581                LabelOp label = SSAUtil.phiIn(getLIR(), block);
582                JumpOp jump = pred == null ? null : SSAUtil.phiOut(getLIR(), pred);
583                for (int i = 0; i < label.getPhiSize(); i++) {
584                    Variable var = asVariable(label.getIncomingValue(i));
585                    TraceInterval toInterval = addVariableDef(var, label, RegisterPriority.ShouldHaveRegister);
586                    // set hint for phis
587                    if (jump != null) {
588                        Value out = jump.getOutgoingValue(i);
589                        if (isVariable(out)) {
590                            TraceInterval fromInterval = allocator.getOrCreateInterval(asVariable(out));
591                            toInterval.setLocationHint(fromInterval);
592                        }
593                    }
594                }
595            }
596        }
597
598        private void handleBlockEnd(AbstractBlockBase<?> block, int opId) {
599            // always alive until the end of the block
600            int aliveOpId = opId + 1;
601            GlobalLivenessInfo livenessInfo = allocator.getGlobalLivenessInfo();
602            for (int varNum : livenessInfo.getBlockOut(block)) {
603                if (allocator.intervalFor(varNum) == null) {
604                    addVariableUse(livenessInfo.getVariable(varNum), 0, aliveOpId, RegisterPriority.None);
605                }
606            }
607        }
608
609        private void numberInstruction(AbstractBlockBase<?> block, LIRInstruction op, int index) {
610            int opId = index << 1;
611            assert op.id() == -1 || op.id() == opId : "must match";
612            op.setId(opId);
613            allocator.putOpIdMaps(index, op, block);
614            assert allocator.instructionForId(opId) == op : "must match";
615        }
616
617        @SuppressWarnings("try")
618        private void addInterTraceHints() {
619            try (DebugContext.Scope s = debug.scope("InterTraceHints", allocator)) {
620                GlobalLivenessInfo livenessInfo = allocator.getGlobalLivenessInfo();
621                // set hints for phi/incoming intervals
622                for (AbstractBlockBase<?> block : sortedBlocks()) {
623                    LabelOp label = (LabelOp) getLIR().getLIRforBlock(block).get(0);
624                    for (AbstractBlockBase<?> pred : block.getPredecessors()) {
625                        addInterTraceHints(livenessInfo, pred, block, label);
626                    }
627                }
628            } catch (Throwable e) {
629                throw debug.handle(e);
630            }
631        }
632
633        private void addInterTraceHints(GlobalLivenessInfo livenessInfo, AbstractBlockBase<?> from, AbstractBlockBase<?> to, LabelOp label) {
634            if (isAllocated(to, from)) {
635                int[] liveVars = livenessInfo.getBlockIn(to);
636                Value[] outLocation = livenessInfo.getOutLocation(from);
637
638                for (int i = 0; i < liveVars.length; i++) {
639                    int varNum = liveVars[i];
640                    TraceInterval toInterval = allocator.intervalFor(varNum);
641                    if (toInterval != null && !toInterval.hasHint()) {
642                        Value fromValue = outLocation[i];
643                        if (!LIRValueUtil.isConstantValue(fromValue)) {
644                            addInterTraceHint(label, varNum, fromValue);
645                        }
646                    }
647                }
648            }
649        }
650
651        private void addInterTraceHint(LabelOp label, int varNum, Value fromValue) {
652            assert isRegister(fromValue) || isVariable(fromValue) || isStackSlotValue(fromValue) || isShadowedRegisterValue(fromValue) : "Wrong fromValue: " + fromValue;
653            TraceInterval to = allocator.intervalFor(varNum);
654            if (to == null) {
655                // variable not live -> do nothing
656                return;
657            }
658            if (isVariableOrRegister(fromValue)) {
659                IntervalHint from = getIntervalHint((AllocatableValue) fromValue);
660                setHint(label, to, from, debug);
661            } else if (isStackSlotValue(fromValue)) {
662                setSpillSlot(label, to, (AllocatableValue) fromValue, debug);
663            } else if (TraceRAshareSpillInformation.getValue(allocator.getLIR().getOptions()) && isShadowedRegisterValue(fromValue)) {
664                ShadowedRegisterValue shadowedRegisterValue = asShadowedRegisterValue(fromValue);
665                IntervalHint from = getIntervalHint(shadowedRegisterValue.getRegister());
666                setHint(label, to, from, debug);
667                setSpillSlot(label, to, shadowedRegisterValue.getStackSlot(), debug);
668            } else {
669                throw GraalError.shouldNotReachHere();
670            }
671        }
672
673        private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from, DebugContext debug) {
674            IntervalHint currentHint = to.locationHint(false);
675            if (currentHint == null) {
676                /*
677                 * Update hint if there was none or if the hint interval starts after the hinted
678                 * interval.
679                 */
680                to.setLocationHint(from);
681                if (debug.isLogEnabled()) {
682                    debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
683                }
684            }
685        }
686
687        private static void setSpillSlot(LIRInstruction op, TraceInterval interval, AllocatableValue spillSlot, DebugContext debug) {
688            if (interval.spillSlot() == null) {
689                interval.setSpillSlot(spillSlot);
690                interval.setSpillState(SpillState.StartInMemory);
691                if (debug.isLogEnabled()) {
692                    debug.log("operation at opId %d: added spill slot %s to interval %s", op.id(), spillSlot, interval);
693                }
694            } else if (debug.isLogEnabled()) {
695                debug.log("operation at opId %d: has already a slot assigned %s", op.id(), interval.spillSlot());
696            }
697        }
698
699        private IntervalHint getIntervalHint(AllocatableValue from) {
700            if (isRegister(from)) {
701                return allocator.getOrCreateFixedInterval(asRegisterValue(from));
702            }
703            return allocator.getOrCreateInterval(asVariable(from));
704        }
705
706    }
707
708    /**
709     * Returns a value for a interval definition, which can be used for re-materialization.
710     *
711     * @param op An instruction which defines a value
712     * @param operand The destination operand of the instruction
713     * @param interval The interval for this defined value.
714     * @return Returns the value which is moved to the instruction and which can be reused at all
715     *         reload-locations in case the interval of this instruction is spilled. Currently this
716     *         can only be a {@link JavaConstant}.
717     */
718    private static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, TraceInterval interval, boolean neverSpillConstants, MoveFactory spillMoveFactory) {
719        if (LoadConstantOp.isLoadConstantOp(op)) {
720            LoadConstantOp move = LoadConstantOp.asLoadConstantOp(op);
721            if (move.getConstant() instanceof JavaConstant) {
722                if (!neverSpillConstants) {
723                    if (!spillMoveFactory.allowConstantToStackMove(move.getConstant())) {
724                        return null;
725                    }
726                    /*
727                     * Check if the interval has any uses which would accept an stack location
728                     * (priority == ShouldHaveRegister). Rematerialization of such intervals can
729                     * result in a degradation, because rematerialization always inserts a constant
730                     * load, even if the value is not needed in a register.
731                     */
732                    int numUsePos = interval.numUsePos();
733                    for (int useIdx = 0; useIdx < numUsePos; useIdx++) {
734                        TraceInterval.RegisterPriority priority = interval.getUsePosRegisterPriority(useIdx);
735                        if (priority == TraceInterval.RegisterPriority.ShouldHaveRegister) {
736                            return null;
737                        }
738                    }
739                }
740                return (JavaConstant) move.getConstant();
741            }
742        }
743        return null;
744    }
745
746}
747