--- /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/TraceLinearScanPhase.java 2016-12-09 00:54:16.420968720 -0800 @@ -0,0 +1,1209 @@ +/* + * Copyright (c) 2009, 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.core.common.GraalOptions.DetailedAsserts; +import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; +import static jdk.vm.ci.code.CodeUtil.isEven; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.asRegisterValue; +import static jdk.vm.ci.code.ValueUtil.isIllegal; +import static jdk.vm.ci.code.ValueUtil.isLegal; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; + +import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; +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.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.ValueConsumer; +import org.graalvm.compiler.lir.Variable; +import org.graalvm.compiler.lir.VirtualStackSlot; +import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase; +import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase.TraceAllocationContext; +import org.graalvm.compiler.lir.alloc.trace.TraceBuilderPhase; +import org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase; +import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; +import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanAllocationPhase.TraceLinearScanAllocationContext; +import org.graalvm.compiler.lir.debug.IntervalDumper; +import org.graalvm.compiler.lir.debug.IntervalDumper.IntervalVisitor; +import org.graalvm.compiler.lir.framemap.FrameMapBuilder; +import org.graalvm.compiler.lir.gen.LIRGenerationResult; +import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory; +import org.graalvm.compiler.lir.phases.LIRPhase; +import org.graalvm.compiler.options.NestedBooleanOptionValue; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionType; +import org.graalvm.compiler.options.OptionValue; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterArray; +import jdk.vm.ci.code.RegisterAttributes; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.Value; +import jdk.vm.ci.meta.ValueKind; + +/** + * Implementation of the Linear Scan allocation approach for traces described in + * "Trace-based Register Allocation in a JIT + * Compiler" by Josef Eisl et al. It is derived from + * "Optimized Interval Splitting in a Linear + * Scan Register Allocator" by Christian Wimmer and Hanspeter Moessenboeck. + */ +public final class TraceLinearScanPhase extends TraceAllocationPhase { + + public static class Options { + // @formatter:off + @Option(help = "Enable spill position optimization", type = OptionType.Debug) + public static final OptionValue LIROptTraceRAEliminateSpillMoves = new NestedBooleanOptionValue(LIRPhase.Options.LIROptimization, true); + // @formatter:on + } + + private static final TraceLinearScanRegisterAllocationPhase TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE = new TraceLinearScanRegisterAllocationPhase(); + private static final TraceLinearScanAssignLocationsPhase TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE = new TraceLinearScanAssignLocationsPhase(); + private static final TraceLinearScanEliminateSpillMovePhase TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE = new TraceLinearScanEliminateSpillMovePhase(); + private static final TraceLinearScanResolveDataFlowPhase TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE = new TraceLinearScanResolveDataFlowPhase(); + private static final TraceLinearScanLifetimeAnalysisPhase TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE = new TraceLinearScanLifetimeAnalysisPhase(); + + public static final int DOMINATOR_SPILL_MOVE_ID = -2; + + private final FrameMapBuilder frameMapBuilder; + private final RegisterAttributes[] registerAttributes; + private final RegisterArray registers; + private final RegisterAllocationConfig regAllocConfig; + private final MoveFactory moveFactory; + + protected final TraceBuilderResult traceBuilderResult; + + private final boolean neverSpillConstants; + + /** + * Maps from {@link Variable#index} to a spill stack slot. If + * {@linkplain org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options#TraceRACacheStackSlots + * enabled} a {@link Variable} is always assigned to the same stack slot. + */ + private final AllocatableValue[] cachedStackSlots; + + private final LIRGenerationResult res; + + public TraceLinearScanPhase(TargetDescription target, LIRGenerationResult res, MoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig, TraceBuilderResult traceBuilderResult, + boolean neverSpillConstants, AllocatableValue[] cachedStackSlots) { + this.res = res; + this.moveFactory = spillMoveFactory; + this.frameMapBuilder = res.getFrameMapBuilder(); + this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap(); + this.regAllocConfig = regAllocConfig; + + this.registers = target.arch.getRegisters(); + this.traceBuilderResult = traceBuilderResult; + this.neverSpillConstants = neverSpillConstants; + this.cachedStackSlots = cachedStackSlots; + + } + + public static boolean isVariableOrRegister(Value value) { + return isVariable(value) || isRegister(value); + } + + abstract static class IntervalPredicate { + + abstract boolean apply(TraceInterval i); + } + + static final IntervalPredicate IS_PRECOLORED_INTERVAL = new IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return isRegister(i.operand); + } + }; + + static final IntervalPredicate IS_VARIABLE_INTERVAL = new IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return isVariable(i.operand); + } + }; + + static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return !isRegister(i.operand); + } + }; + + public TraceLinearScan createAllocator(Trace trace) { + return new TraceLinearScan(trace); + } + + @Override + protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, TraceAllocationContext traceContext) { + createAllocator(trace).allocate(target, lirGenRes, traceContext); + } + + private static boolean isSortedByFrom(T[] intervals) { + int from = -1; + for (T interval : intervals) { + assert interval != null; + assert from <= interval.from(); + from = interval.from(); + } + return true; + } + + private static boolean isSortedBySpillPos(TraceInterval[] intervals) { + int from = -1; + for (TraceInterval interval : intervals) { + assert interval != null; + assert from <= interval.spillDefinitionPos(); + from = interval.spillDefinitionPos(); + } + return true; + } + + private static T[] sortIntervalsBeforeAllocation(T[] intervals, T[] sortedList) { + int sortedIdx = 0; + int sortedFromMax = -1; + + // special sorting algorithm: the original interval-list is almost sorted, + // only some intervals are swapped. So this is much faster than a complete QuickSort + for (T interval : intervals) { + if (interval != null) { + int from = interval.from(); + + if (sortedFromMax <= from) { + sortedList[sortedIdx++] = interval; + sortedFromMax = interval.from(); + } else { + // the assumption that the intervals are already sorted failed, + // so this interval must be sorted in manually + int j; + for (j = sortedIdx - 1; j >= 0 && from < sortedList[j].from(); j--) { + sortedList[j + 1] = sortedList[j]; + } + sortedList[j + 1] = interval; + sortedIdx++; + } + } + } + return sortedList; + } + + public final class TraceLinearScan implements IntervalDumper { + + /** + * Intervals sorted by {@link TraceInterval#from()}. + */ + private TraceInterval[] sortedIntervals; + + /** + * Fixed intervals sorted by {@link FixedInterval#from()}. + */ + private FixedInterval[] sortedFixedIntervals; + + private final Trace trace; + + public TraceLinearScan(Trace trace) { + this.trace = trace; + this.fixedIntervals = new FixedInterval[registers.size()]; + } + + /** + * Converts an operand (variable or register) to an index in a flat address space covering + * all the {@linkplain Variable variables} and {@linkplain RegisterValue registers} being + * processed by this allocator. + */ + int operandNumber(Value operand) { + assert !isRegister(operand) : "Register do not have operand numbers: " + operand; + assert isVariable(operand) : "Unsupported Value " + operand; + return ((Variable) operand).index; + } + + /** + * Gets the number of operands. This value will increase by 1 for new variable. + */ + int operandSize() { + return getLIR().numVariables(); + } + + /** + * Gets the number of registers. This value will never change. + */ + int numRegisters() { + return registers.size(); + } + + public int getFirstLirInstructionId(AbstractBlockBase block) { + int result = getLIR().getLIRforBlock(block).get(0).id(); + assert result >= 0; + return result; + } + + public int getLastLirInstructionId(AbstractBlockBase block) { + List instructions = getLIR().getLIRforBlock(block); + int result = instructions.get(instructions.size() - 1).id(); + assert result >= 0; + return result; + } + + /** + * Gets an object describing the attributes of a given register according to this register + * configuration. + */ + public RegisterAttributes attributes(Register reg) { + return registerAttributes[reg.number]; + } + + public MoveFactory getSpillMoveFactory() { + return moveFactory; + } + + protected TraceLocalMoveResolver createMoveResolver() { + TraceLocalMoveResolver moveResolver = new TraceLocalMoveResolver(this); + assert moveResolver.checkEmpty(); + return moveResolver; + } + + void assignSpillSlot(TraceInterval interval) { + /* + * Assign the canonical spill slot of the parent (if a part of the interval is already + * spilled) or allocate a new spill slot. + */ + if (interval.canMaterialize()) { + interval.assignLocation(Value.ILLEGAL); + } else if (interval.spillSlot() != null) { + interval.assignLocation(interval.spillSlot()); + } else { + AllocatableValue slot = allocateSpillSlot(interval); + interval.setSpillSlot(slot); + interval.assignLocation(slot); + } + } + + /** + * Returns a new spill slot or a cached entry if there is already one for the + * {@linkplain TraceInterval#operand variable}. + */ + private AllocatableValue allocateSpillSlot(TraceInterval interval) { + int variableIndex = LIRValueUtil.asVariable(interval.splitParent().operand).index; + if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue()) { + AllocatableValue cachedStackSlot = cachedStackSlots[variableIndex]; + if (cachedStackSlot != null) { + TraceRegisterAllocationPhase.globalStackSlots.increment(); + assert cachedStackSlot.getValueKind().equals(interval.kind()) : "CachedStackSlot: kind mismatch? " + interval.kind() + " vs. " + cachedStackSlot.getValueKind(); + return cachedStackSlot; + } + } + VirtualStackSlot slot = frameMapBuilder.allocateSpillSlot(interval.kind()); + if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue()) { + cachedStackSlots[variableIndex] = slot; + } + TraceRegisterAllocationPhase.allocatedStackSlots.increment(); + return slot; + } + + // access to block list (sorted in linear scan order) + public int blockCount() { + return sortedBlocks().length; + } + + public AbstractBlockBase blockAt(int index) { + return sortedBlocks()[index]; + } + + int numLoops() { + return getLIR().getControlFlowGraph().getLoops().size(); + } + + boolean isBlockBegin(int opId) { + return opId == 0 || blockForId(opId) != blockForId(opId - 1); + } + + boolean isBlockEnd(int opId) { + boolean isBlockBegin = isBlockBegin(opId + 2); + assert isBlockBegin == (instructionForId(opId & (~1)) instanceof BlockEndOp); + return isBlockBegin; + } + + boolean coversBlockBegin(int opId1, int opId2) { + return blockForId(opId1) != blockForId(opId2); + } + + /** + * Determines if an {@link LIRInstruction} destroys all caller saved registers. + * + * @param opId an instruction {@linkplain LIRInstruction#id id} + * @return {@code true} if the instruction denoted by {@code id} destroys all caller saved + * registers. + */ + boolean hasCall(int opId) { + assert isEven(opId) : "opId not even"; + return instructionForId(opId).destroysCallerSavedRegisters(); + } + + public boolean isProcessed(Value operand) { + return !isRegister(operand) || attributes(asRegister(operand)).isAllocatable(); + } + + // * Phase 5: actual register allocation + + private TraceInterval addToList(TraceInterval first, TraceInterval prev, TraceInterval interval) { + TraceInterval newFirst = first; + if (prev != null) { + prev.next = interval; + } else { + newFirst = interval; + } + return newFirst; + } + + TraceInterval createUnhandledListByFrom(IntervalPredicate isList1) { + assert isSortedByFrom(sortedIntervals) : "interval list is not sorted"; + return createUnhandledList(isList1); + } + + TraceInterval createUnhandledListBySpillPos(IntervalPredicate isList1) { + assert isSortedBySpillPos(sortedIntervals) : "interval list is not sorted"; + return createUnhandledList(isList1); + } + + private TraceInterval createUnhandledList(IntervalPredicate isList1) { + + TraceInterval list1 = TraceInterval.EndMarker; + + TraceInterval list1Prev = null; + TraceInterval v; + + int n = sortedIntervals.length; + for (int i = 0; i < n; i++) { + v = sortedIntervals[i]; + if (v == null) { + continue; + } + + if (isList1.apply(v)) { + list1 = addToList(list1, list1Prev, v); + list1Prev = v; + } + } + + if (list1Prev != null) { + list1Prev.next = TraceInterval.EndMarker; + } + + assert list1Prev == null || list1Prev.next == TraceInterval.EndMarker : "linear list ends not with sentinel"; + + return list1; + } + + private FixedInterval addToList(FixedInterval first, FixedInterval prev, FixedInterval interval) { + FixedInterval newFirst = first; + if (prev != null) { + prev.next = interval; + } else { + newFirst = interval; + } + return newFirst; + } + + FixedInterval createFixedUnhandledList() { + assert isSortedByFrom(sortedFixedIntervals) : "interval list is not sorted"; + + FixedInterval list1 = FixedInterval.EndMarker; + + FixedInterval list1Prev = null; + FixedInterval v; + + int n = sortedFixedIntervals.length; + for (int i = 0; i < n; i++) { + v = sortedFixedIntervals[i]; + if (v == null) { + continue; + } + + v.rewindRange(); + list1 = addToList(list1, list1Prev, v); + list1Prev = v; + } + + if (list1Prev != null) { + list1Prev.next = FixedInterval.EndMarker; + } + + assert list1Prev == null || list1Prev.next == FixedInterval.EndMarker : "linear list ends not with sentinel"; + + return list1; + } + + // SORTING + + protected void sortIntervalsBeforeAllocation() { + int sortedLen = 0; + for (TraceInterval interval : intervals()) { + if (interval != null) { + sortedLen++; + } + } + sortedIntervals = TraceLinearScanPhase.sortIntervalsBeforeAllocation(intervals(), new TraceInterval[sortedLen]); + } + + protected void sortFixedIntervalsBeforeAllocation() { + int sortedLen = 0; + for (FixedInterval interval : fixedIntervals()) { + if (interval != null) { + sortedLen++; + } + } + sortedFixedIntervals = TraceLinearScanPhase.sortIntervalsBeforeAllocation(fixedIntervals(), new FixedInterval[sortedLen]); + } + + void sortIntervalsAfterAllocation() { + if (hasDerivedIntervals()) { + // no intervals have been added during allocation, so sorted list is already up to + // date + return; + } + + TraceInterval[] oldList = sortedIntervals; + TraceInterval[] newList = Arrays.copyOfRange(intervals(), firstDerivedIntervalIndex(), intervalsSize()); + int oldLen = oldList.length; + int newLen = newList.length; + + // conventional sort-algorithm for new intervals + Arrays.sort(newList, (TraceInterval a, TraceInterval b) -> a.from() - b.from()); + + // merge old and new list (both already sorted) into one combined list + TraceInterval[] combinedList = new TraceInterval[oldLen + newLen]; + int oldIdx = 0; + int newIdx = 0; + + while (oldIdx + newIdx < combinedList.length) { + if (newIdx >= newLen || (oldIdx < oldLen && oldList[oldIdx].from() <= newList[newIdx].from())) { + combinedList[oldIdx + newIdx] = oldList[oldIdx]; + oldIdx++; + } else { + combinedList[oldIdx + newIdx] = newList[newIdx]; + newIdx++; + } + } + + sortedIntervals = combinedList; + } + + void sortIntervalsBySpillPos() { + // TODO (JE): better algorithm? + // conventional sort-algorithm for new intervals + Arrays.sort(sortedIntervals, (TraceInterval a, TraceInterval b) -> a.spillDefinitionPos() - b.spillDefinitionPos()); + } + + // wrapper for Interval.splitChildAtOpId that performs a bailout in product mode + // instead of returning null + public TraceInterval splitChildAtOpId(TraceInterval interval, int opId, LIRInstruction.OperandMode mode) { + TraceInterval result = interval.getSplitChildAtOpId(opId, mode); + + if (result != null) { + if (Debug.isLogEnabled()) { + Debug.log("Split child at pos %d of interval %s is %s", opId, interval, result); + } + return result; + } + throw new GraalError("LinearScan: interval is null"); + } + + AllocatableValue canonicalSpillOpr(TraceInterval interval) { + assert interval.spillSlot() != null : "canonical spill slot not set"; + return interval.spillSlot(); + } + + boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) { + TraceInterval interval = intervalFor(operand); + assert interval != null : "interval must exist"; + + if (opId != -1) { + /* + * Operands are not changed when an interval is split during allocation, so search + * the right interval here. + */ + interval = splitChildAtOpId(interval, opId, mode); + } + + return isIllegal(interval.location()) && interval.canMaterialize(); + } + + boolean isCallerSave(Value operand) { + return attributes(asRegister(operand)).isCallerSave(); + } + + @SuppressWarnings("try") + protected void allocate(TargetDescription target, LIRGenerationResult lirGenRes, TraceAllocationContext traceContext) { + /* + * This is the point to enable debug logging for the whole register allocation. + */ + try (Indent indent = Debug.logAndIndent("LinearScan allocate")) { + TraceLinearScanAllocationContext context = new TraceLinearScanAllocationContext(traceContext.spillMoveFactory, traceContext.registerAllocationConfig, traceBuilderResult, this); + + TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE.apply(target, lirGenRes, trace, context, false); + + try (Scope s = Debug.scope("AfterLifetimeAnalysis", this)) { + + printLir("Before register allocation", true); + printIntervals("Before register allocation"); + + sortIntervalsBeforeAllocation(); + sortFixedIntervalsBeforeAllocation(); + + TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE.apply(target, lirGenRes, trace, context, false); + printIntervals("After register allocation"); + + // resolve intra-trace data-flow + TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE.apply(target, lirGenRes, trace, context, false); + Debug.dump(TraceBuilderPhase.TRACE_DUMP_LEVEL, sortedBlocks(), "%s", TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE.getName()); + + // eliminate spill moves + if (Options.LIROptTraceRAEliminateSpillMoves.getValue()) { + TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE.apply(target, lirGenRes, trace, context, false); + Debug.dump(TraceBuilderPhase.TRACE_DUMP_LEVEL, sortedBlocks(), "%s", TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE.getName()); + } + + TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE.apply(target, lirGenRes, trace, context, false); + + if (DetailedAsserts.getValue()) { + verifyIntervals(); + } + } catch (Throwable e) { + throw Debug.handle(e); + } + } + } + + public void printLir(String label, @SuppressWarnings("unused") boolean hirValid) { + if (Debug.isDumpEnabled(TraceBuilderPhase.TRACE_DUMP_LEVEL)) { + Debug.dump(TraceBuilderPhase.TRACE_DUMP_LEVEL, sortedBlocks(), label); + } + } + + boolean verify() { + // (check that all intervals have a correct register and that no registers are + // overwritten) + verifyIntervals(); + + verifyRegisters(); + + Debug.log("no errors found"); + + return true; + } + + @SuppressWarnings("try") + private void verifyRegisters() { + // Enable this logging to get output for the verification process. + try (Indent indent = Debug.logAndIndent("verifying register allocation")) { + RegisterVerifier verifier = new RegisterVerifier(this); + verifier.verify(blockAt(0)); + } + } + + @SuppressWarnings("try") + protected void verifyIntervals() { + try (Indent indent = Debug.logAndIndent("verifying intervals")) { + int len = intervalsSize(); + + for (int i = 0; i < len; i++) { + final TraceInterval i1 = intervals()[i]; + if (i1 == null) { + continue; + } + + i1.checkSplitChildren(); + + if (i1.operandNumber != i) { + Debug.log("Interval %d is on position %d in list", i1.operandNumber, i); + Debug.log(i1.logString()); + throw new GraalError(""); + } + + if (isVariable(i1.operand) && i1.kind().equals(LIRKind.Illegal)) { + Debug.log("Interval %d has no type assigned", i1.operandNumber); + Debug.log(i1.logString()); + throw new GraalError(""); + } + + if (i1.location() == null) { + Debug.log("Interval %d has no register assigned", i1.operandNumber); + Debug.log(i1.logString()); + throw new GraalError(""); + } + + if (i1.isEmpty()) { + Debug.log("Interval %d has no Range", i1.operandNumber); + Debug.log(i1.logString()); + throw new GraalError(""); + } + + if (i1.from() >= i1.to()) { + Debug.log("Interval %d has zero length range", i1.operandNumber); + Debug.log(i1.logString()); + throw new GraalError(""); + } + + // special intervals that are created in MoveResolver + // . ignore them because the range information has no meaning there + if (i1.from() == 1 && i1.to() == 2) { + continue; + } + // check any intervals + for (int j = i + 1; j < len; j++) { + final TraceInterval i2 = intervals()[j]; + if (i2 == null) { + continue; + } + + // special intervals that are created in MoveResolver + // . ignore them because the range information has no meaning there + if (i2.from() == 1 && i2.to() == 2) { + continue; + } + Value l1 = i1.location(); + Value l2 = i2.location(); + boolean intersects = i1.intersects(i2); + if (intersects && !isIllegal(l1) && (l1.equals(l2))) { + throw GraalError.shouldNotReachHere(String.format("Intervals %s and %s overlap and have the same register assigned\n%s\n%s", i1, i2, i1.logString(), i2.logString())); + } + } + // check fixed intervals + for (FixedInterval i2 : fixedIntervals()) { + if (i2 == null) { + continue; + } + + Value l1 = i1.location(); + Value l2 = i2.location(); + boolean intersects = i2.intersects(i1); + if (intersects && !isIllegal(l1) && (l1.equals(l2))) { + throw GraalError.shouldNotReachHere(String.format("Intervals %s and %s overlap and have the same register assigned\n%s\n%s", i1, i2, i1.logString(), i2.logString())); + } + } + } + } + } + + class CheckConsumer implements ValueConsumer { + + boolean ok; + FixedInterval curInterval; + + @Override + public void visitValue(Value operand, OperandMode mode, EnumSet flags) { + if (isRegister(operand)) { + if (fixedIntervalFor(asRegisterValue(operand)) == curInterval) { + ok = true; + } + } + } + } + + @SuppressWarnings("try") + void verifyNoOopsInFixedIntervals() { + try (Indent indent = Debug.logAndIndent("verifying that no oops are in fixed intervals *")) { + CheckConsumer checkConsumer = new CheckConsumer(); + + TraceInterval otherIntervals; + FixedInterval fixedInts = createFixedUnhandledList(); + // to ensure a walking until the last instruction id, add a dummy interval + // with a high operation id + otherIntervals = new TraceInterval(Value.ILLEGAL, -1); + otherIntervals.addRange(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1); + TraceIntervalWalker iw = new TraceIntervalWalker(this, fixedInts, otherIntervals); + + for (AbstractBlockBase block : sortedBlocks()) { + List instructions = getLIR().getLIRforBlock(block); + + for (int j = 0; j < instructions.size(); j++) { + LIRInstruction op = instructions.get(j); + + if (op.hasState()) { + iw.walkBefore(op.id()); + boolean checkLive = true; + + /* + * Make sure none of the fixed registers is live across an oopmap since + * we can't handle that correctly. + */ + if (checkLive) { + for (FixedInterval interval = iw.activeFixedList.getFixed(); interval != FixedInterval.EndMarker; interval = interval.next) { + if (interval.to() > op.id() + 1) { + /* + * This interval is live out of this op so make sure that + * this interval represents some value that's referenced by + * this op either as an input or output. + */ + checkConsumer.curInterval = interval; + checkConsumer.ok = false; + + op.visitEachInput(checkConsumer); + op.visitEachAlive(checkConsumer); + op.visitEachTemp(checkConsumer); + op.visitEachOutput(checkConsumer); + + assert checkConsumer.ok : "fixed intervals should never be live across an oopmap point"; + } + } + } + } + } + } + } + } + + public LIR getLIR() { + return res.getLIR(); + } + + public FrameMapBuilder getFrameMapBuilder() { + return frameMapBuilder; + } + + public AbstractBlockBase[] sortedBlocks() { + return trace.getBlocks(); + } + + public RegisterArray getRegisters() { + return registers; + } + + public RegisterAllocationConfig getRegisterAllocationConfig() { + return regAllocConfig; + } + + public boolean callKillsRegisters() { + return regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved(); + } + + boolean neverSpillConstants() { + return neverSpillConstants; + } + + // IntervalData + + private static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1; + + /** + * The index of the first entry in {@link #intervals} for a + * {@linkplain #createDerivedInterval(TraceInterval) derived interval}. + */ + private int firstDerivedIntervalIndex = -1; + + /** + * @see #fixedIntervals() + */ + private final FixedInterval[] fixedIntervals; + + /** + * @see #intervals() + */ + private TraceInterval[] intervals; + + /** + * The number of valid entries in {@link #intervals}. + */ + private int intervalsSize; + + /** + * Map from an instruction {@linkplain LIRInstruction#id id} to the instruction. Entries + * should be retrieved with {@link #instructionForId(int)} as the id is not simply an index + * into this array. + */ + private LIRInstruction[] opIdToInstructionMap; + + /** + * Map from an instruction {@linkplain LIRInstruction#id id} to the + * {@linkplain AbstractBlockBase block} containing the instruction. Entries should be + * retrieved with {@link #blockForId(int)} as the id is not simply an index into this array. + */ + private AbstractBlockBase[] opIdToBlockMap; + + /** + * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + */ + TraceInterval[] intervals() { + return intervals; + } + + /** + * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + */ + FixedInterval[] fixedIntervals() { + return fixedIntervals; + } + + void initIntervals() { + intervalsSize = operandSize(); + intervals = new TraceInterval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)]; + } + + /** + * Creates a new fixed interval. + * + * @param reg the operand for the interval + * @return the created interval + */ + private FixedInterval createFixedInterval(RegisterValue reg) { + FixedInterval interval = new FixedInterval(reg); + int operandNumber = reg.getRegister().number; + assert fixedIntervals[operandNumber] == null; + fixedIntervals[operandNumber] = interval; + return interval; + } + + /** + * Creates a new interval. + * + * @param operand the operand for the interval + * @return the created interval + */ + private TraceInterval createInterval(AllocatableValue operand) { + assert isLegal(operand); + int operandNumber = operandNumber(operand); + TraceInterval interval = new TraceInterval(operand, operandNumber); + assert operandNumber < intervalsSize; + assert intervals[operandNumber] == null; + intervals[operandNumber] = interval; + return interval; + } + + /** + * Creates an interval as a result of splitting or spilling another interval. + * + * @param source an interval being split of spilled + * @return a new interval derived from {@code source} + */ + TraceInterval createDerivedInterval(TraceInterval source) { + if (firstDerivedIntervalIndex == -1) { + firstDerivedIntervalIndex = intervalsSize; + } + if (intervalsSize == intervals.length) { + intervals = Arrays.copyOf(intervals, intervals.length + (intervals.length >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT) + 1); + } + // increments intervalsSize + Variable variable = createVariable(source.kind()); + + assert intervalsSize <= intervals.length; + + TraceInterval interval = createInterval(variable); + assert intervals[intervalsSize - 1] == interval; + return interval; + } + + /** + * Creates a new variable for a derived interval. Note that the variable is not + * {@linkplain LIR#numVariables() managed} so it must not be inserted into the {@link LIR}. + */ + private Variable createVariable(ValueKind kind) { + return new Variable(kind, intervalsSize++); + } + + boolean hasDerivedIntervals() { + return firstDerivedIntervalIndex != -1; + } + + int firstDerivedIntervalIndex() { + return firstDerivedIntervalIndex; + } + + public int intervalsSize() { + return intervalsSize; + } + + FixedInterval fixedIntervalFor(RegisterValue reg) { + return fixedIntervals[reg.getRegister().number]; + } + + FixedInterval getOrCreateFixedInterval(RegisterValue reg) { + FixedInterval ret = fixedIntervalFor(reg); + if (ret == null) { + return createFixedInterval(reg); + } else { + return ret; + } + } + + TraceInterval intervalFor(Value operand) { + int operandNumber = operandNumber(operand); + assert operandNumber < intervalsSize; + return intervals[operandNumber]; + } + + TraceInterval getOrCreateInterval(AllocatableValue operand) { + TraceInterval ret = intervalFor(operand); + if (ret == null) { + return createInterval(operand); + } else { + return ret; + } + } + + void initOpIdMaps(int numInstructions) { + opIdToInstructionMap = new LIRInstruction[numInstructions]; + opIdToBlockMap = new AbstractBlockBase[numInstructions]; + } + + void putOpIdMaps(int index, LIRInstruction op, AbstractBlockBase block) { + opIdToInstructionMap[index] = op; + opIdToBlockMap[index] = block; + } + + /** + * Gets the highest instruction id allocated by this object. + */ + int maxOpId() { + assert opIdToInstructionMap.length > 0 : "no operations"; + return (opIdToInstructionMap.length - 1) << 1; + } + + /** + * Converts an {@linkplain LIRInstruction#id instruction id} to an instruction index. All + * LIR instructions in a method have an index one greater than their linear-scan order + * predecessor with the first instruction having an index of 0. + */ + private int opIdToIndex(int opId) { + return opId >> 1; + } + + /** + * Retrieves the {@link LIRInstruction} based on its {@linkplain LIRInstruction#id id}. + * + * @param opId an instruction {@linkplain LIRInstruction#id id} + * @return the instruction whose {@linkplain LIRInstruction#id} {@code == id} + */ + LIRInstruction instructionForId(int opId) { + assert isEven(opId) : "opId not even"; + LIRInstruction instr = opIdToInstructionMap[opIdToIndex(opId)]; + assert instr.id() == opId; + return instr; + } + + /** + * Gets the block containing a given instruction. + * + * @param opId an instruction {@linkplain LIRInstruction#id id} + * @return the block containing the instruction denoted by {@code opId} + */ + AbstractBlockBase blockForId(int opId) { + assert opIdToBlockMap.length > 0 && opId >= 0 && opId <= maxOpId() + 1 : "opId out of range: " + opId; + return opIdToBlockMap[opIdToIndex(opId)]; + } + + @SuppressWarnings("try") + public void printIntervals(String label) { + if (Debug.isDumpEnabled(TraceBuilderPhase.TRACE_DUMP_LEVEL)) { + if (Debug.isLogEnabled()) { + try (Indent indent = Debug.logAndIndent("intervals %s", label)) { + for (FixedInterval interval : fixedIntervals) { + if (interval != null) { + Debug.log("%s", interval.logString()); + } + } + + for (TraceInterval interval : intervals) { + if (interval != null) { + Debug.log("%s", interval.logString()); + } + } + + try (Indent indent2 = Debug.logAndIndent("Basic Blocks")) { + for (AbstractBlockBase block : trace.getBlocks()) { + Debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop()); + } + } + } + } + Debug.dump(Debug.INFO_LOG_LEVEL, this, label); + } + } + + @Override + public void visitIntervals(IntervalVisitor visitor) { + for (FixedInterval interval : fixedIntervals) { + if (interval != null) { + printFixedInterval(interval, visitor); + } + } + for (TraceInterval interval : intervals) { + if (interval != null) { + printInterval(interval, visitor); + } + } + } + + } + + public static boolean verifyEquals(TraceLinearScan a, TraceLinearScan b) { + assert compareFixed(a.fixedIntervals(), b.fixedIntervals()); + assert compareIntervals(a.intervals(), b.intervals()); + return true; + } + + private static boolean compareIntervals(TraceInterval[] a, TraceInterval[] b) { + for (int i = 0; i < Math.max(a.length, b.length); i++) { + if (i >= a.length) { + assert b[i] == null : "missing a interval: " + i + " b: " + b[i]; + continue; + } + if (i >= b.length) { + assert a[i] == null : "missing b interval: " + i + " a: " + a[i]; + continue; + } + compareInterval(a[i], b[i]); + } + return true; + } + + private static void compareInterval(TraceInterval a, TraceInterval b) { + if (a == null) { + assert b == null : "First interval is null but second is: " + b; + return; + } + assert b != null : "Second interval is null but forst is: " + a; + assert a.operand.equals(b.operand) : "Operand mismatch: " + a + " vs. " + b; + assert a.from() == b.from() : "From mismatch: " + a + " vs. " + b; + assert a.to() == b.to() : "To mismatch: " + a + " vs. " + b; + assert verifyIntervalsEquals(a, b); + } + + private static boolean verifyIntervalsEquals(TraceInterval a, TraceInterval b) { + for (int i = 0; i < Math.max(a.numUsePos(), b.numUsePos()); i++) { + assert i < a.numUsePos() : "missing a usepos: " + i + " b: " + b; + assert i < b.numUsePos() : "missing b usepos: " + i + " a: " + a; + int aPos = a.getUsePos(i); + int bPos = b.getUsePos(i); + assert aPos == bPos : "Use Positions differ: " + aPos + " vs. " + bPos; + RegisterPriority aReg = a.getUsePosRegisterPriority(i); + RegisterPriority bReg = b.getUsePosRegisterPriority(i); + assert aReg == bReg : "Register priority differ: " + aReg + " vs. " + bReg; + } + return true; + } + + private static boolean compareFixed(FixedInterval[] a, FixedInterval[] b) { + for (int i = 0; i < Math.max(a.length, b.length); i++) { + if (i >= a.length) { + assert b[i] == null : "missing a interval: " + i + " b: " + b[i]; + continue; + } + if (i >= b.length) { + assert a[i] == null : "missing b interval: " + i + " a: " + a[i]; + continue; + } + compareFixedInterval(a[i], b[i]); + } + return true; + } + + private static void compareFixedInterval(FixedInterval a, FixedInterval b) { + if (a == null) { + assert b == null || isEmptyInterval(b) : "First interval is null but second is: " + b; + return; + } + if (b == null) { + assert isEmptyInterval(a) : "Second interval is null but first is: " + a; + return; + } + assert a.operand.equals(b.operand) : "Operand mismatch: " + a + " vs. " + b; + assert a.from() == b.from() : "From mismatch: " + a + " vs. " + b; + assert a.to() == b.to() : "To mismatch: " + a + " vs. " + b; + assert verifyFixeEquas(a, b); + } + + private static boolean verifyFixeEquas(FixedInterval a, FixedInterval b) { + a.rewindRange(); + b.rewindRange(); + while (!a.currentAtEnd()) { + assert !b.currentAtEnd() : "Fixed range mismatch: " + a + " vs. " + b; + assert a.currentFrom() == b.currentFrom() : "From range mismatch: " + a + " vs. " + b + " from: " + a.currentFrom() + " vs. " + b.currentFrom(); + assert a.currentTo() == b.currentTo() : "To range mismatch: " + a + " vs. " + b + " from: " + a.currentTo() + " vs. " + b.currentTo(); + a.nextRange(); + b.nextRange(); + } + assert b.currentAtEnd() : "Fixed range mismatch: " + a + " vs. " + b; + return true; + } + + private static boolean isEmptyInterval(FixedInterval fixed) { + return fixed.from() == -1 && fixed.to() == 0; + } + + private static void printFixedInterval(FixedInterval interval, IntervalVisitor visitor) { + Value hint = null; + AllocatableValue operand = interval.operand; + String type = "fixed"; + visitor.visitIntervalStart(operand, operand, operand, hint, type); + + // print ranges + for (FixedRange range = interval.first(); range != FixedRange.EndMarker; range = range.next) { + visitor.visitRange(range.from, range.to); + } + + // no use positions + + visitor.visitIntervalEnd("NOT_SUPPORTED"); + + } + + private static void printInterval(TraceInterval interval, IntervalVisitor visitor) { + Value hint = interval.locationHint(false) != null ? interval.locationHint(false).location() : null; + AllocatableValue operand = interval.operand; + String type = isRegister(operand) ? "fixed" : operand.getValueKind().getPlatformKind().toString(); + visitor.visitIntervalStart(interval.splitParent().operand, operand, interval.location(), hint, type); + + // print ranges + visitor.visitRange(interval.from(), interval.to()); + + // print use positions + int prev = -1; + for (int i = interval.numUsePos() - 1; i >= 0; --i) { + assert prev < interval.getUsePos(i) : "use positions not sorted"; + visitor.visitUsePos(interval.getUsePos(i), interval.getUsePosRegisterPriority(i)); + prev = interval.getUsePos(i); + } + + visitor.visitIntervalEnd(interval.spillState()); + } +}