--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java 2016-12-09 00:54:16.147956686 -0800 @@ -0,0 +1,689 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.lir.alloc.trace.lsra; + +import static org.graalvm.compiler.lir.LIRValueUtil.asVariable; +import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; +import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; +import static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation; +import static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAuseInterTraceHints; +import static org.graalvm.compiler.lir.alloc.trace.TraceUtil.asShadowedRegisterValue; +import static org.graalvm.compiler.lir.alloc.trace.TraceUtil.isShadowedRegisterValue; +import static org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.isVariableOrRegister; +import static jdk.vm.ci.code.ValueUtil.asRegisterValue; +import static jdk.vm.ci.code.ValueUtil.asStackSlot; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import static jdk.vm.ci.code.ValueUtil.isStackSlot; + +import java.util.EnumSet; +import java.util.List; +import java.util.ListIterator; + +import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.alloc.Trace; +import org.graalvm.compiler.core.common.alloc.TraceBuilderResult; +import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.Debug.Scope; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.lir.InstructionValueConsumer; +import org.graalvm.compiler.lir.LIR; +import org.graalvm.compiler.lir.LIRInstruction; +import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; +import org.graalvm.compiler.lir.LIRInstruction.OperandMode; +import org.graalvm.compiler.lir.LIRValueUtil; +import org.graalvm.compiler.lir.StandardOp.BlockEndOp; +import org.graalvm.compiler.lir.StandardOp.LabelOp; +import org.graalvm.compiler.lir.StandardOp.LoadConstantOp; +import org.graalvm.compiler.lir.StandardOp.ValueMoveOp; +import org.graalvm.compiler.lir.ValueProcedure; +import org.graalvm.compiler.lir.Variable; +import org.graalvm.compiler.lir.alloc.trace.ShadowedRegisterValue; +import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; +import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.SpillState; +import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; +import org.graalvm.compiler.lir.gen.LIRGenerationResult; +import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory; +import org.graalvm.compiler.lir.ssi.SSIUtil; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterArray; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.Value; + +public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanAllocationPhase { + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, TraceLinearScanAllocationContext context) { + TraceBuilderResult traceBuilderResult = context.resultTraces; + TraceLinearScan allocator = context.allocator; + new Analyser(allocator, traceBuilderResult).analyze(); + } + + public static final class Analyser { + private static final int DUMP_DURING_ANALYSIS_LEVEL = 4; + private final TraceLinearScan allocator; + private final TraceBuilderResult traceBuilderResult; + private int numInstructions; + + public Analyser(TraceLinearScan allocator, TraceBuilderResult traceBuilderResult) { + this.allocator = allocator; + this.traceBuilderResult = traceBuilderResult; + } + + private AbstractBlockBase[] sortedBlocks() { + return allocator.sortedBlocks(); + } + + private LIR getLIR() { + return allocator.getLIR(); + } + + private RegisterArray getCallerSavedRegisters() { + return allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters(); + } + + public void analyze() { + countInstructions(); + buildIntervals(); + } + + private boolean sameTrace(AbstractBlockBase a, AbstractBlockBase b) { + return traceBuilderResult.getTraceForBlock(b) == traceBuilderResult.getTraceForBlock(a); + } + + private boolean isAllocatedOrCurrent(AbstractBlockBase currentBlock, AbstractBlockBase other) { + return traceBuilderResult.getTraceForBlock(other).getId() <= traceBuilderResult.getTraceForBlock(currentBlock).getId(); + } + + /** + * Count instructions in all blocks. The numbering follows the + * {@linkplain TraceLinearScan#sortedBlocks() register allocation order}. + */ + private void countInstructions() { + + allocator.initIntervals(); + + int numberInstructions = 0; + for (AbstractBlockBase block : sortedBlocks()) { + numberInstructions += getLIR().getLIRforBlock(block).size(); + } + numInstructions = numberInstructions; + + // initialize with correct length + allocator.initOpIdMaps(numberInstructions); + } + + private final InstructionValueConsumer outputConsumer = new InstructionValueConsumer() { + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { + if (isVariableOrRegister(operand)) { + addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op)); + addRegisterHint(op, operand, mode, flags, true); + } + } + }; + + private final InstructionValueConsumer tempConsumer = new InstructionValueConsumer() { + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { + if (isVariableOrRegister(operand)) { + addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister); + addRegisterHint(op, operand, mode, flags, false); + } + } + }; + private final InstructionValueConsumer aliveConsumer = new InstructionValueConsumer() { + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { + if (isVariableOrRegister(operand)) { + RegisterPriority p = registerPriorityOfInputOperand(flags); + int opId = op.id(); + int blockFrom = 0; + addUse((AllocatableValue) operand, blockFrom, opId + 1, p); + addRegisterHint(op, operand, mode, flags, false); + } + } + }; + + private final InstructionValueConsumer inputConsumer = new InstructionValueConsumer() { + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { + if (isVariableOrRegister(operand)) { + int opId = op.id(); + RegisterPriority p = registerPriorityOfInputOperand(flags); + int blockFrom = 0; + addUse((AllocatableValue) operand, blockFrom, opId, p); + addRegisterHint(op, operand, mode, flags, false); + } + } + + }; + + private final InstructionValueConsumer stateProc = new InstructionValueConsumer() { + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { + if (isVariableOrRegister(operand)) { + int opId = op.id(); + int blockFrom = 0; + addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None); + } + } + }; + + private void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority) { + if (!allocator.isProcessed(operand)) { + return; + } + if (isRegister(operand)) { + addFixedUse(asRegisterValue(operand), from, to); + } else { + assert isVariable(operand) : operand; + addVariableUse(asVariable(operand), from, to, registerPriority); + } + } + + private void addFixedUse(RegisterValue reg, int from, int to) { + FixedInterval interval = allocator.getOrCreateFixedInterval(reg); + interval.addRange(from, to); + if (Debug.isLogEnabled()) { + Debug.log("add fixed use: %s, at %d", interval, to); + } + } + + private void addVariableUse(Variable operand, int from, int to, RegisterPriority registerPriority) { + TraceInterval interval = allocator.getOrCreateInterval(operand); + interval.addRange(from, to); + + // Register use position at even instruction id. + interval.addUsePos(to & ~1, registerPriority); + + if (Debug.isLogEnabled()) { + Debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name()); + } + } + + private void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority) { + if (!allocator.isProcessed(operand)) { + return; + } + if (isRegister(operand)) { + addFixedDef(asRegisterValue(operand), op); + } else { + assert isVariable(operand) : operand; + addVariableDef(asVariable(operand), op, registerPriority); + } + } + + private void addFixedDef(RegisterValue reg, LIRInstruction op) { + FixedInterval interval = allocator.getOrCreateFixedInterval(reg); + int defPos = op.id(); + if (interval.from() <= defPos) { + /* + * Update the starting point (when a range is first created for a use, its start is + * the beginning of the current block until a def is encountered). + */ + interval.setFrom(defPos); + + } else { + /* + * Dead value - make vacuous interval also add register priority for dead intervals + */ + interval.addRange(defPos, defPos + 1); + if (Debug.isLogEnabled()) { + Debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos); + } + } + if (Debug.isLogEnabled()) { + Debug.log("add fixed def: %s, at %d", interval, defPos); + } + } + + private void addVariableDef(Variable operand, LIRInstruction op, RegisterPriority registerPriority) { + int defPos = op.id(); + + TraceInterval interval = allocator.getOrCreateInterval(operand); + + if (interval.isEmpty()) { + /* + * Dead value - make vacuous interval also add register priority for dead intervals + */ + interval.addRange(defPos, defPos + 1); + if (Debug.isLogEnabled()) { + Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); + } + } else { + /* + * Update the starting point (when a range is first created for a use, its start is + * the beginning of the current block until a def is encountered). + */ + interval.setFrom(defPos); + } + if (!(op instanceof LabelOp)) { + // no use positions for labels + interval.addUsePos(defPos, registerPriority); + } + + changeSpillDefinitionPos(op, operand, interval, defPos); + if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) { + // detection of method-parameters and roundfp-results + interval.setSpillState(SpillState.StartInMemory); + } + interval.addMaterializationValue(getMaterializedValue(op, operand, interval, allocator.neverSpillConstants(), allocator.getSpillMoveFactory())); + + if (Debug.isLogEnabled()) { + Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); + } + } + + private void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority) { + if (!allocator.isProcessed(operand)) { + return; + } + if (isRegister(operand)) { + addFixedTemp(asRegisterValue(operand), tempPos); + } else { + assert isVariable(operand) : operand; + addVariableTemp(asVariable(operand), tempPos, registerPriority); + } + } + + private void addFixedTemp(RegisterValue reg, int tempPos) { + FixedInterval interval = allocator.getOrCreateFixedInterval(reg); + interval.addRange(tempPos, tempPos + 1); + if (Debug.isLogEnabled()) { + Debug.log("add fixed temp: %s, at %d", interval, tempPos); + } + } + + private void addVariableTemp(Variable operand, int tempPos, RegisterPriority registerPriority) { + TraceInterval interval = allocator.getOrCreateInterval(operand); + + if (interval.isEmpty()) { + interval.addRange(tempPos, tempPos + 1); + } else if (interval.from() > tempPos) { + interval.setFrom(tempPos); + } + + interval.addUsePos(tempPos, registerPriority); + interval.addMaterializationValue(null); + + if (Debug.isLogEnabled()) { + Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name()); + } + } + + /** + * Eliminates moves from register to stack if the stack slot is known to be correct. + * + * @param op + * @param operand + */ + private void changeSpillDefinitionPos(LIRInstruction op, AllocatableValue operand, TraceInterval interval, int defPos) { + assert interval.isSplitParent() : "can only be called for split parents"; + + switch (interval.spillState()) { + case NoDefinitionFound: + // assert interval.spillDefinitionPos() == -1 : "must no be set before"; + interval.setSpillDefinitionPos(defPos); + if (!(op instanceof LabelOp)) { + // Do not update state for labels. This will be done afterwards. + interval.setSpillState(SpillState.NoSpillStore); + } + break; + + case NoSpillStore: + assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created"; + if (defPos < interval.spillDefinitionPos() - 2) { + /* + * Second definition found, so no spill optimization possible for this + * interval. + */ + interval.setSpillState(SpillState.NoOptimization); + } else { + // two consecutive definitions (because of two-operand LIR form) + assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal"; + } + break; + + case NoOptimization: + // nothing to do + break; + + default: + throw GraalError.shouldNotReachHere("other states not allowed at this time"); + } + } + + private void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { + if (flags.contains(OperandFlag.HINT) && isVariableOrRegister(targetValue)) { + + ValueProcedure registerHintProc = new ValueProcedure() { + @Override + public Value doValue(Value registerHint, OperandMode valueMode, EnumSet valueFlags) { + if (isVariableOrRegister(registerHint)) { + /* + * TODO (je): clean up + */ + final AllocatableValue fromValue; + final AllocatableValue toValue; + /* hints always point from def to use */ + if (hintAtDef) { + fromValue = (AllocatableValue) registerHint; + toValue = (AllocatableValue) targetValue; + } else { + fromValue = (AllocatableValue) targetValue; + toValue = (AllocatableValue) registerHint; + } + Debug.log("addRegisterHint %s to %s", fromValue, toValue); + final TraceInterval to; + final IntervalHint from; + if (isRegister(toValue)) { + if (isRegister(fromValue)) { + // fixed to fixed move + return null; + } + from = getIntervalHint(toValue); + to = allocator.getOrCreateInterval(fromValue); + } else { + to = allocator.getOrCreateInterval(toValue); + from = getIntervalHint(fromValue); + } + + to.setLocationHint(from); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to); + } + + return registerHint; + } + return null; + } + }; + op.forEachRegisterHint(targetValue, mode, registerHintProc); + } + } + + private static boolean optimizeMethodArgument(Value value) { + /* + * Object method arguments that are passed on the stack are currently not optimized + * because this requires that the runtime visits method arguments during stack walking. + */ + return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && LIRKind.isValue(value); + } + + /** + * Determines the register priority for an instruction's output/result operand. + */ + private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) { + if (op instanceof LabelOp) { + // skip method header + return RegisterPriority.None; + } + if (op instanceof ValueMoveOp) { + ValueMoveOp move = (ValueMoveOp) op; + if (optimizeMethodArgument(move.getInput())) { + return RegisterPriority.None; + } + } + + // all other operands require a register + return RegisterPriority.MustHaveRegister; + } + + /** + * Determines the priority which with an instruction's input operand will be allocated a + * register. + */ + private static RegisterPriority registerPriorityOfInputOperand(EnumSet flags) { + if (flags.contains(OperandFlag.OUTGOING)) { + return RegisterPriority.None; + } + if (flags.contains(OperandFlag.STACK)) { + return RegisterPriority.ShouldHaveRegister; + } + // all other operands require a register + return RegisterPriority.MustHaveRegister; + } + + @SuppressWarnings("try") + private void buildIntervals() { + + try (Indent indent = Debug.logAndIndent("build intervals")) { + + // create a list with all caller-save registers (cpu, fpu, xmm) + RegisterArray callerSaveRegs = getCallerSavedRegisters(); + int instructionIndex = numInstructions; + + // iterate all blocks in reverse order + AbstractBlockBase[] blocks = sortedBlocks(); + for (int i = blocks.length - 1; i >= 0; i--) { + final AbstractBlockBase block = blocks[i]; + + try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) { + + /* + * Iterate all instructions of the block in reverse order. definitions of + * intervals are processed before uses. + */ + List instructions = getLIR().getLIRforBlock(block); + ListIterator instIt = instructions.listIterator(instructions.size()); + while (instIt.hasPrevious()) { + final LIRInstruction op = instIt.previous(); + // number instruction + instructionIndex--; + final int opId = instructionIndex << 1; + numberInstruction(block, op, instructionIndex); + + try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) { + + /* + * Add a temp range for each register if operation destroys + * caller-save registers. + */ + if (op.destroysCallerSavedRegisters()) { + for (Register r : callerSaveRegs) { + if (allocator.attributes(r).isAllocatable()) { + addTemp(r.asValue(), opId, RegisterPriority.None); + } + } + if (Debug.isLogEnabled()) { + Debug.log("operation destroys all caller-save registers"); + } + } + + op.visitEachOutput(outputConsumer); + op.visitEachTemp(tempConsumer); + op.visitEachAlive(aliveConsumer); + op.visitEachInput(inputConsumer); + + /* + * Add uses of live locals from interpreter's point of view for + * proper debug information generation. Treat these operands as temp + * values (if the live range is extended to a call site, the value + * would be in a register at the call otherwise). + */ + op.visitEachState(stateProc); + } + + } // end of instruction iteration + } + if (Debug.isDumpEnabled(DUMP_DURING_ANALYSIS_LEVEL)) { + allocator.printIntervals("After Block " + block); + } + } // end of block iteration + assert instructionIndex == 0 : "not at start?" + instructionIndex; + + // fix spill state for phi/sigma intervals + for (TraceInterval interval : allocator.intervals()) { + if (interval != null && interval.spillState().equals(SpillState.NoDefinitionFound) && interval.spillDefinitionPos() != -1) { + // there was a definition in a phi/sigma + interval.setSpillState(SpillState.NoSpillStore); + } + } + if (TraceRAuseInterTraceHints.getValue()) { + addInterTraceHints(); + } + for (FixedInterval interval1 : allocator.fixedIntervals()) { + if (interval1 != null) { + /* We use [-1, 0] to avoid intersection with incoming values. */ + interval1.addRange(-1, 0); + } + } + } + } + + private void numberInstruction(AbstractBlockBase block, LIRInstruction op, int index) { + int opId = index << 1; + assert op.id() == -1 || op.id() == opId : "must match"; + op.setId(opId); + allocator.putOpIdMaps(index, op, block); + assert allocator.instructionForId(opId) == op : "must match"; + } + + @SuppressWarnings("try") + private void addInterTraceHints() { + try (Scope s = Debug.scope("InterTraceHints", allocator)) { + // set hints for phi/sigma intervals + for (AbstractBlockBase block : sortedBlocks()) { + LabelOp label = SSIUtil.incoming(getLIR(), block); + for (AbstractBlockBase pred : block.getPredecessors()) { + if (isAllocatedOrCurrent(block, pred)) { + BlockEndOp outgoing = SSIUtil.outgoing(getLIR(), pred); + // do not look at phi variables as they are not same value! + for (int i = outgoing.getPhiSize(); i < outgoing.getOutgoingSize(); i++) { + Value toValue = label.getIncomingValue(i); + assert !isShadowedRegisterValue(toValue) : "Shadowed Registers are not allowed here: " + toValue; + if (isVariable(toValue)) { + Value fromValue = outgoing.getOutgoingValue(i); + assert sameTrace(block, pred) || !isVariable(fromValue) : "Unallocated variable: " + fromValue; + if (!LIRValueUtil.isConstantValue(fromValue)) { + addInterTraceHint(label, (AllocatableValue) toValue, fromValue); + } + } + } + } + } + } + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + private void addInterTraceHint(LabelOp label, AllocatableValue toValue, Value fromValue) { + assert isVariable(toValue) : "Wrong toValue: " + toValue; + assert isRegister(fromValue) || isVariable(fromValue) || isStackSlotValue(fromValue) || isShadowedRegisterValue(fromValue) : "Wrong fromValue: " + fromValue; + TraceInterval to = allocator.getOrCreateInterval(toValue); + if (isVariableOrRegister(fromValue)) { + IntervalHint from = getIntervalHint((AllocatableValue) fromValue); + setHint(label, to, from); + } else if (isStackSlotValue(fromValue)) { + setSpillSlot(label, to, (AllocatableValue) fromValue); + } else if (TraceRAshareSpillInformation.getValue() && isShadowedRegisterValue(fromValue)) { + ShadowedRegisterValue shadowedRegisterValue = asShadowedRegisterValue(fromValue); + IntervalHint from = getIntervalHint(shadowedRegisterValue.getRegister()); + setHint(label, to, from); + setSpillSlot(label, to, shadowedRegisterValue.getStackSlot()); + } else { + throw GraalError.shouldNotReachHere(); + } + } + + private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from) { + IntervalHint currentHint = to.locationHint(false); + if (currentHint == null) { + /* + * Update hint if there was none or if the hint interval starts after the hinted + * interval. + */ + to.setLocationHint(from); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to); + } + } + } + + private static void setSpillSlot(LIRInstruction op, TraceInterval interval, AllocatableValue spillSlot) { + if (interval.spillSlot() == null) { + interval.setSpillSlot(spillSlot); + interval.setSpillState(SpillState.StartInMemory); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added spill slot %s to interval %s", op.id(), spillSlot, interval); + } + } else if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: has already a slot assigned %s", op.id(), interval.spillSlot()); + } + } + + private IntervalHint getIntervalHint(AllocatableValue from) { + if (isRegister(from)) { + return allocator.getOrCreateFixedInterval(asRegisterValue(from)); + } + return allocator.getOrCreateInterval(from); + } + + } + + /** + * Returns a value for a interval definition, which can be used for re-materialization. + * + * @param op An instruction which defines a value + * @param operand The destination operand of the instruction + * @param interval The interval for this defined value. + * @return Returns the value which is moved to the instruction and which can be reused at all + * reload-locations in case the interval of this instruction is spilled. Currently this + * can only be a {@link JavaConstant}. + */ + private static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, TraceInterval interval, boolean neverSpillConstants, MoveFactory spillMoveFactory) { + if (op instanceof LoadConstantOp) { + LoadConstantOp move = (LoadConstantOp) op; + if (move.getConstant() instanceof JavaConstant) { + if (!neverSpillConstants) { + if (!spillMoveFactory.allowConstantToStackMove(move.getConstant())) { + return null; + } + /* + * Check if the interval has any uses which would accept an stack location + * (priority == ShouldHaveRegister). Rematerialization of such intervals can + * result in a degradation, because rematerialization always inserts a constant + * load, even if the value is not needed in a register. + */ + int numUsePos = interval.numUsePos(); + for (int useIdx = 0; useIdx < numUsePos; useIdx++) { + TraceInterval.RegisterPriority priority = interval.getUsePosRegisterPriority(useIdx); + if (priority == TraceInterval.RegisterPriority.ShouldHaveRegister) { + return null; + } + } + } + return (JavaConstant) move.getConstant(); + } + } + return null; + } + +}