/* * Copyright (c) 2009, 2015, 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 org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.Indent; import org.graalvm.compiler.lir.alloc.trace.lsra.FixedInterval.FixedList; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.AnyList; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterBinding; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.State; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; /** */ class TraceIntervalWalker { protected final TraceLinearScan allocator; /** * Sorted list of intervals, not live before the current position. */ protected AnyList unhandledAnyList; /** * Sorted list of intervals, live at the current position. */ protected AnyList activeAnyList; protected FixedList activeFixedList; /** * Sorted list of intervals in a life time hole at the current position. */ protected FixedList inactiveFixedList; /** * The current position (intercept point through the intervals). */ protected int currentPosition; /** * Processes the {@code currentInterval} interval in an attempt to allocate a physical register * to it and thus allow it to be moved to a list of {@linkplain #activeAnyList active} * intervals. * * @param currentInterval The interval to be activated. * * @return {@code true} if a register was allocated to the {@code currentInterval} interval */ protected boolean activateCurrent(TraceInterval currentInterval) { if (Debug.isLogEnabled()) { logCurrentStatus(); } return true; } @SuppressWarnings("try") protected void logCurrentStatus() { try (Indent i = Debug.logAndIndent("active:")) { logList(activeFixedList.getFixed()); logList(activeAnyList.getAny()); } try (Indent i = Debug.logAndIndent("inactive(fixed):")) { logList(inactiveFixedList.getFixed()); } } private static void logList(FixedInterval i) { for (FixedInterval interval = i; interval != FixedInterval.EndMarker; interval = interval.next) { Debug.log("%s", interval.logString()); } } private static void logList(TraceInterval i) { for (TraceInterval interval = i; interval != TraceInterval.EndMarker; interval = interval.next) { Debug.log("%s", interval.logString()); } } void walkBefore(int lirOpId) { walkTo(lirOpId - 1); } void walk() { walkTo(Integer.MAX_VALUE); } /** * Creates a new interval walker. * * @param allocator the register allocator context * @param unhandledFixed the list of unhandled {@linkplain RegisterBinding#Fixed fixed} * intervals * @param unhandledAny the list of unhandled {@linkplain RegisterBinding#Any non-fixed} * intervals */ TraceIntervalWalker(TraceLinearScan allocator, FixedInterval unhandledFixed, TraceInterval unhandledAny) { this.allocator = allocator; unhandledAnyList = new AnyList(unhandledAny); activeAnyList = new AnyList(TraceInterval.EndMarker); activeFixedList = new FixedList(FixedInterval.EndMarker); // we don't need a separate unhandled list for fixed. inactiveFixedList = new FixedList(unhandledFixed); currentPosition = -1; } protected void removeFromList(TraceInterval interval) { activeAnyList.removeAny(interval); } /** * Walks up to {@code from} and updates the state of {@link FixedInterval fixed intervals}. * * Fixed intervals can switch back and forth between the states {@link State#Active} and * {@link State#Inactive} (and eventually to {@link State#Handled} but handled intervals are not * managed). */ @SuppressWarnings("try") private void walkToFixed(State state, int from) { assert state == State.Active || state == State.Inactive : "wrong state"; FixedInterval prevprev = null; FixedInterval prev = (state == State.Active) ? activeFixedList.getFixed() : inactiveFixedList.getFixed(); FixedInterval next = prev; if (Debug.isLogEnabled()) { try (Indent i = Debug.logAndIndent("walkToFixed(%s, %d):", state, from)) { logList(next); } } while (next.currentFrom() <= from) { FixedInterval cur = next; next = cur.next; boolean rangeHasChanged = false; while (cur.currentTo() <= from) { cur.nextRange(); rangeHasChanged = true; } // also handle move from inactive list to active list rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from); if (rangeHasChanged) { // remove cur from list if (prevprev == null) { if (state == State.Active) { activeFixedList.setFixed(next); } else { inactiveFixedList.setFixed(next); } } else { prevprev.next = next; } prev = next; TraceInterval.State newState; if (cur.currentAtEnd()) { // move to handled state (not maintained as a list) newState = State.Handled; } else { if (cur.currentFrom() <= from) { // sort into active list activeFixedList.addToListSortedByCurrentFromPositions(cur); newState = State.Active; } else { // sort into inactive list inactiveFixedList.addToListSortedByCurrentFromPositions(cur); newState = State.Inactive; } if (prev == cur) { assert state == newState; prevprev = prev; prev = cur.next; } } intervalMoved(cur, state, newState); } else { prevprev = prev; prev = cur.next; } } } /** * Walks up to {@code from} and updates the state of {@link TraceInterval intervals}. * * Trace intervals can switch once from {@link State#Unhandled} to {@link State#Active} and then * to {@link State#Handled} but handled intervals are not managed. */ @SuppressWarnings("try") private void walkToAny(int from) { TraceInterval prevprev = null; TraceInterval prev = activeAnyList.getAny(); TraceInterval next = prev; if (Debug.isLogEnabled()) { try (Indent i = Debug.logAndIndent("walkToAny(%d):", from)) { logList(next); } } while (next.from() <= from) { TraceInterval cur = next; next = cur.next; if (cur.to() <= from) { // remove cur from list if (prevprev == null) { activeAnyList.setAny(next); } else { prevprev.next = next; } intervalMoved(cur, State.Active, State.Handled); } else { prevprev = prev; } prev = next; } } /** * Get the next interval from {@linkplain #unhandledAnyList} which starts before or at * {@code toOpId}. The returned interval is removed. * * @postcondition all intervals in {@linkplain #unhandledAnyList} start after {@code toOpId}. * * @return The next interval or null if there is no {@linkplain #unhandledAnyList unhandled} * interval at position {@code toOpId}. */ private TraceInterval nextInterval(int toOpId) { TraceInterval any = unhandledAnyList.getAny(); if (any != TraceInterval.EndMarker) { TraceInterval currentInterval = unhandledAnyList.getAny(); if (toOpId < currentInterval.from()) { return null; } unhandledAnyList.setAny(currentInterval.next); currentInterval.next = TraceInterval.EndMarker; return currentInterval; } return null; } /** * Walk up to {@code toOpId}. * * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeFixedList} * and {@link #inactiveFixedList} are populated. */ @SuppressWarnings("try") protected void walkTo(int toOpId) { assert currentPosition <= toOpId : "can not walk backwards"; for (TraceInterval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) { int opId = currentInterval.from(); // set currentPosition prior to call of walkTo currentPosition = opId; // update unhandled stack intervals // updateUnhandledStackIntervals(opId); // call walkTo even if currentPosition == id walkToFixed(State.Active, opId); walkToFixed(State.Inactive, opId); walkToAny(opId); try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) { if (activateCurrent(currentInterval)) { activeAnyList.addToListSortedByFromPositions(currentInterval); intervalMoved(currentInterval, State.Unhandled, State.Active); } } } // set currentPosition prior to call of walkTo currentPosition = toOpId; if (currentPosition <= allocator.maxOpId()) { // update unhandled stack intervals // updateUnhandledStackIntervals(toOpId); // call walkTo if still in range walkToFixed(State.Active, toOpId); walkToFixed(State.Inactive, toOpId); walkToAny(toOpId); } } private static void intervalMoved(IntervalHint interval, State from, State to) { // intervalMoved() is called whenever an interval moves from one interval list to another. // In the implementation of this method it is prohibited to move the interval to any list. if (Debug.isLogEnabled()) { Debug.log("interval moved from %s to %s: %s", from, to, interval.logString()); } } }