--- /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/stackslotalloc/LSStackSlotAllocator.java 2016-12-09 00:54:33.771733526 -0800
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2014, 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.stackslotalloc;
+
+import static org.graalvm.compiler.lir.LIRValueUtil.asVirtualStackSlot;
+import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
+import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+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.DebugCloseable;
+import org.graalvm.compiler.debug.DebugTimer;
+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.ValueProcedure;
+import org.graalvm.compiler.lir.VirtualStackSlot;
+import org.graalvm.compiler.lir.framemap.FrameMapBuilderTool;
+import org.graalvm.compiler.lir.framemap.SimpleVirtualStackSlot;
+import org.graalvm.compiler.lir.framemap.VirtualStackSlotRange;
+import org.graalvm.compiler.lir.gen.LIRGenerationResult;
+import org.graalvm.compiler.lir.phases.AllocationPhase;
+import org.graalvm.compiler.options.NestedBooleanOptionValue;
+import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionType;
+
+import jdk.vm.ci.code.StackSlot;
+import jdk.vm.ci.code.TargetDescription;
+import jdk.vm.ci.meta.Value;
+import jdk.vm.ci.meta.ValueKind;
+
+/**
+ * Linear Scan {@link StackSlotAllocatorUtil stack slot allocator}.
+ *
+ * Remark: The analysis works under the assumption that a stack slot is no longer live after
+ * its last usage. If an {@link LIRInstruction instruction} transfers the raw address of the stack
+ * slot to another location, e.g. a registers, and this location is referenced later on, the
+ * {@link org.graalvm.compiler.lir.LIRInstruction.Use usage} of the stack slot must be marked with
+ * the {@link OperandFlag#UNINITIALIZED}. Otherwise the stack slot might be reused and its content
+ * destroyed.
+ */
+public final class LSStackSlotAllocator extends AllocationPhase {
+
+ public static class Options {
+ // @formatter:off
+ @Option(help = "Use linear scan stack slot allocation.", type = OptionType.Debug)
+ public static final NestedBooleanOptionValue LIROptLSStackSlotAllocator = new NestedBooleanOptionValue(LIROptimization, true);
+ // @formatter:on
+ }
+
+ private static final DebugTimer MainTimer = Debug.timer("LSStackSlotAllocator");
+ private static final DebugTimer NumInstTimer = Debug.timer("LSStackSlotAllocator[NumberInstruction]");
+ private static final DebugTimer BuildIntervalsTimer = Debug.timer("LSStackSlotAllocator[BuildIntervals]");
+ private static final DebugTimer VerifyIntervalsTimer = Debug.timer("LSStackSlotAllocator[VerifyIntervals]");
+ private static final DebugTimer AllocateSlotsTimer = Debug.timer("LSStackSlotAllocator[AllocateSlots]");
+ private static final DebugTimer AssignSlotsTimer = Debug.timer("LSStackSlotAllocator[AssignSlots]");
+
+ @Override
+ protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) {
+ allocateStackSlots((FrameMapBuilderTool) lirGenRes.getFrameMapBuilder(), lirGenRes);
+ lirGenRes.buildFrameMap();
+ }
+
+ @SuppressWarnings("try")
+ public static void allocateStackSlots(FrameMapBuilderTool builder, LIRGenerationResult res) {
+ if (builder.getNumberOfStackSlots() > 0) {
+ try (DebugCloseable t = MainTimer.start()) {
+ new Allocator(res.getLIR(), builder).allocate();
+ }
+ }
+ }
+
+ private static final class Allocator {
+ private final LIR lir;
+ private final FrameMapBuilderTool frameMapBuilder;
+ private final StackInterval[] stackSlotMap;
+ private final PriorityQueue unhandled;
+ private final PriorityQueue active;
+ private final AbstractBlockBase>[] sortedBlocks;
+ private final int maxOpId;
+
+ @SuppressWarnings("try")
+ private Allocator(LIR lir, FrameMapBuilderTool frameMapBuilder) {
+ this.lir = lir;
+ this.frameMapBuilder = frameMapBuilder;
+ this.stackSlotMap = new StackInterval[frameMapBuilder.getNumberOfStackSlots()];
+ this.sortedBlocks = lir.getControlFlowGraph().getBlocks();
+
+ // insert by from
+ this.unhandled = new PriorityQueue<>((a, b) -> a.from() - b.from());
+ // insert by to
+ this.active = new PriorityQueue<>((a, b) -> a.to() - b.to());
+
+ try (DebugCloseable t = NumInstTimer.start()) {
+ // step 1: number instructions
+ this.maxOpId = numberInstructions(lir, sortedBlocks);
+ }
+ }
+
+ @SuppressWarnings("try")
+ private void allocate() {
+ Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After StackSlot numbering");
+
+ long currentFrameSize = StackSlotAllocatorUtil.allocatedFramesize.isEnabled() ? frameMapBuilder.getFrameMap().currentFrameSize() : 0;
+ Set usePos;
+ // step 2: build intervals
+ try (Scope s = Debug.scope("StackSlotAllocationBuildIntervals"); Indent indent = Debug.logAndIndent("BuildIntervals"); DebugCloseable t = BuildIntervalsTimer.start()) {
+ usePos = buildIntervals();
+ }
+ // step 3: verify intervals
+ if (Debug.isEnabled()) {
+ try (DebugCloseable t = VerifyIntervalsTimer.start()) {
+ assert verifyIntervals();
+ }
+ }
+ if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+ dumpIntervals("Before stack slot allocation");
+ }
+ // step 4: allocate stack slots
+ try (DebugCloseable t = AllocateSlotsTimer.start()) {
+ allocateStackSlots();
+ }
+ if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) {
+ dumpIntervals("After stack slot allocation");
+ }
+
+ // step 5: assign stack slots
+ try (DebugCloseable t = AssignSlotsTimer.start()) {
+ assignStackSlots(usePos);
+ }
+ Debug.dump(Debug.INFO_LOG_LEVEL, lir, "After StackSlot assignment");
+ if (StackSlotAllocatorUtil.allocatedFramesize.isEnabled()) {
+ StackSlotAllocatorUtil.allocatedFramesize.add(frameMapBuilder.getFrameMap().currentFrameSize() - currentFrameSize);
+ }
+ }
+
+ // ====================
+ // step 1: number instructions
+ // ====================
+
+ /**
+ * Numbers all instructions in all blocks.
+ *
+ * @return The id of the last operation.
+ */
+ private static int numberInstructions(LIR lir, AbstractBlockBase>[] sortedBlocks) {
+ int opId = 0;
+ int index = 0;
+ for (AbstractBlockBase> block : sortedBlocks) {
+
+ List instructions = lir.getLIRforBlock(block);
+
+ int numInst = instructions.size();
+ for (int j = 0; j < numInst; j++) {
+ LIRInstruction op = instructions.get(j);
+ op.setId(opId);
+
+ index++;
+ opId += 2; // numbering of lirOps by two
+ }
+ }
+ assert (index << 1) == opId : "must match: " + (index << 1);
+ return opId - 2;
+ }
+
+ // ====================
+ // step 2: build intervals
+ // ====================
+
+ private Set buildIntervals() {
+ return new FixPointIntervalBuilder(lir, stackSlotMap, maxOpId()).build();
+ }
+
+ // ====================
+ // step 3: verify intervals
+ // ====================
+
+ private boolean verifyIntervals() {
+ for (StackInterval interval : stackSlotMap) {
+ if (interval != null) {
+ assert interval.verify(maxOpId());
+ }
+ }
+ return true;
+ }
+
+ // ====================
+ // step 4: allocate stack slots
+ // ====================
+
+ @SuppressWarnings("try")
+ private void allocateStackSlots() {
+ // create unhandled lists
+ for (StackInterval interval : stackSlotMap) {
+ if (interval != null) {
+ unhandled.add(interval);
+ }
+ }
+
+ for (StackInterval current = activateNext(); current != null; current = activateNext()) {
+ try (Indent indent = Debug.logAndIndent("allocate %s", current)) {
+ allocateSlot(current);
+ }
+ }
+
+ }
+
+ private void allocateSlot(StackInterval current) {
+ VirtualStackSlot virtualSlot = current.getOperand();
+ final StackSlot location;
+ if (virtualSlot instanceof VirtualStackSlotRange) {
+ // No reuse of ranges (yet).
+ VirtualStackSlotRange slotRange = (VirtualStackSlotRange) virtualSlot;
+ location = frameMapBuilder.getFrameMap().allocateStackSlots(slotRange.getSlots(), slotRange.getObjects());
+ StackSlotAllocatorUtil.virtualFramesize.add(frameMapBuilder.getFrameMap().spillSlotRangeSize(slotRange.getSlots()));
+ StackSlotAllocatorUtil.allocatedSlots.increment();
+ } else {
+ assert virtualSlot instanceof SimpleVirtualStackSlot : "Unexpected VirtualStackSlot type: " + virtualSlot;
+ StackSlot slot = findFreeSlot((SimpleVirtualStackSlot) virtualSlot);
+ if (slot != null) {
+ /*
+ * Free stack slot available. Note that we create a new one because the kind
+ * might not match.
+ */
+ location = StackSlot.get(current.kind(), slot.getRawOffset(), slot.getRawAddFrameSize());
+ StackSlotAllocatorUtil.reusedSlots.increment();
+ Debug.log(Debug.BASIC_LOG_LEVEL, "Reuse stack slot %s (reallocated from %s) for virtual stack slot %s", location, slot, virtualSlot);
+ } else {
+ // Allocate new stack slot.
+ location = frameMapBuilder.getFrameMap().allocateSpillSlot(virtualSlot.getValueKind());
+ StackSlotAllocatorUtil.virtualFramesize.add(frameMapBuilder.getFrameMap().spillSlotSize(virtualSlot.getValueKind()));
+ StackSlotAllocatorUtil.allocatedSlots.increment();
+ Debug.log(Debug.BASIC_LOG_LEVEL, "New stack slot %s for virtual stack slot %s", location, virtualSlot);
+ }
+ }
+ Debug.log("Allocate location %s for interval %s", location, current);
+ current.setLocation(location);
+ }
+
+ private enum SlotSize {
+ Size1,
+ Size2,
+ Size4,
+ Size8,
+ Illegal;
+ }
+
+ private SlotSize forKind(ValueKind> kind) {
+ switch (frameMapBuilder.getFrameMap().spillSlotSize(kind)) {
+ case 1:
+ return SlotSize.Size1;
+ case 2:
+ return SlotSize.Size2;
+ case 4:
+ return SlotSize.Size4;
+ case 8:
+ return SlotSize.Size8;
+ default:
+ return SlotSize.Illegal;
+ }
+ }
+
+ private EnumMap> freeSlots;
+
+ /**
+ * @return The list of free stack slots for {@code size} or {@code null} if there is none.
+ */
+ private Deque getOrNullFreeSlots(SlotSize size) {
+ if (freeSlots == null) {
+ return null;
+ }
+ return freeSlots.get(size);
+ }
+
+ /**
+ * @return the list of free stack slots for {@code size}. If there is none a list is
+ * created.
+ */
+ private Deque getOrInitFreeSlots(SlotSize size) {
+ assert size != SlotSize.Illegal;
+ Deque freeList;
+ if (freeSlots != null) {
+ freeList = freeSlots.get(size);
+ } else {
+ freeSlots = new EnumMap<>(SlotSize.class);
+ freeList = null;
+ }
+ if (freeList == null) {
+ freeList = new ArrayDeque<>();
+ freeSlots.put(size, freeList);
+ }
+ assert freeList != null;
+ return freeList;
+ }
+
+ /**
+ * Gets a free stack slot for {@code slot} or {@code null} if there is none.
+ */
+ private StackSlot findFreeSlot(SimpleVirtualStackSlot slot) {
+ assert slot != null;
+ SlotSize size = forKind(slot.getValueKind());
+ if (size == SlotSize.Illegal) {
+ return null;
+ }
+ Deque freeList = getOrNullFreeSlots(size);
+ if (freeList == null) {
+ return null;
+ }
+ return freeList.pollLast();
+ }
+
+ /**
+ * Adds a stack slot to the list of free slots.
+ */
+ private void freeSlot(StackSlot slot) {
+ SlotSize size = forKind(slot.getValueKind());
+ if (size == SlotSize.Illegal) {
+ return;
+ }
+ getOrInitFreeSlots(size).addLast(slot);
+ }
+
+ /**
+ * Gets the next unhandled interval and finishes handled intervals.
+ */
+ private StackInterval activateNext() {
+ if (unhandled.isEmpty()) {
+ return null;
+ }
+ StackInterval next = unhandled.poll();
+ // finish handled intervals
+ for (int id = next.from(); activePeekId() < id;) {
+ finished(active.poll());
+ }
+ Debug.log("active %s", next);
+ active.add(next);
+ return next;
+ }
+
+ /**
+ * Gets the lowest {@link StackInterval#to() end position} of all active intervals. If there
+ * is none {@link Integer#MAX_VALUE} is returned.
+ */
+ private int activePeekId() {
+ StackInterval first = active.peek();
+ if (first == null) {
+ return Integer.MAX_VALUE;
+ }
+ return first.to();
+ }
+
+ /**
+ * Finishes {@code interval} by adding its location to the list of free stack slots.
+ */
+ private void finished(StackInterval interval) {
+ StackSlot location = interval.location();
+ Debug.log("finished %s (freeing %s)", interval, location);
+ freeSlot(location);
+ }
+
+ // ====================
+ // step 5: assign stack slots
+ // ====================
+
+ private void assignStackSlots(Set usePos) {
+ for (LIRInstruction op : usePos) {
+ op.forEachInput(assignSlot);
+ op.forEachAlive(assignSlot);
+ op.forEachState(assignSlot);
+
+ op.forEachTemp(assignSlot);
+ op.forEachOutput(assignSlot);
+ }
+ }
+
+ ValueProcedure assignSlot = new ValueProcedure() {
+ @Override
+ public Value doValue(Value value, OperandMode mode, EnumSet flags) {
+ if (isVirtualStackSlot(value)) {
+ VirtualStackSlot slot = asVirtualStackSlot(value);
+ StackInterval interval = get(slot);
+ assert interval != null;
+ return interval.location();
+ }
+ return value;
+ }
+ };
+
+ // ====================
+ //
+ // ====================
+
+ /**
+ * Gets the highest instruction id.
+ */
+ private int maxOpId() {
+ return maxOpId;
+ }
+
+ private StackInterval get(VirtualStackSlot stackSlot) {
+ return stackSlotMap[stackSlot.getId()];
+ }
+
+ private void dumpIntervals(String label) {
+ Debug.dump(Debug.INFO_LOG_LEVEL, new StackIntervalDumper(Arrays.copyOf(stackSlotMap, stackSlotMap.length)), label);
+ }
+
+ }
+}