/* * Copyright (c) 2009, 2011, 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.lsra; import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.Indent; import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBinding; import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBindingLists; import org.graalvm.compiler.lir.alloc.lsra.Interval.State; /** */ public class IntervalWalker { protected final LinearScan allocator; /** * Sorted list of intervals, not live before the current position. */ protected RegisterBindingLists unhandledLists; /** * Sorted list of intervals, live at the current position. */ protected RegisterBindingLists activeLists; /** * Sorted list of intervals in a life time hole at the current position. */ protected RegisterBindingLists inactiveLists; /** * The current position (intercept point through the intervals). */ protected int currentPosition; /** * The binding of the current interval being processed. */ protected RegisterBinding currentBinding; /** * 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 #activeLists active} intervals. * * @return {@code true} if a register was allocated to the {@code currentInterval} interval */ protected boolean activateCurrent(@SuppressWarnings({"unused"}) Interval currentInterval) { return true; } 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 */ IntervalWalker(LinearScan allocator, Interval unhandledFixed, Interval unhandledAny) { this.allocator = allocator; unhandledLists = new RegisterBindingLists(unhandledFixed, unhandledAny, allocator.intervalEndMarker); activeLists = new RegisterBindingLists(allocator.intervalEndMarker, allocator.intervalEndMarker, allocator.intervalEndMarker); inactiveLists = new RegisterBindingLists(allocator.intervalEndMarker, allocator.intervalEndMarker, allocator.intervalEndMarker); currentPosition = -1; } protected void removeFromList(Interval interval) { if (interval.state == State.Active) { activeLists.remove(RegisterBinding.Any, interval); } else { assert interval.state == State.Inactive : "invalid state"; inactiveLists.remove(RegisterBinding.Any, interval); } } private void walkTo(State state, int from) { assert state == State.Active || state == State.Inactive : "wrong state"; for (RegisterBinding binding : RegisterBinding.VALUES) { walkTo(state, from, binding); } } private void walkTo(State state, int from, RegisterBinding binding) { Interval prevprev = null; Interval prev = (state == State.Active) ? activeLists.get(binding) : inactiveLists.get(binding); Interval next = prev; while (next.currentFrom() <= from) { Interval 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) { activeLists.set(binding, next); } else { inactiveLists.set(binding, next); } } else { prevprev.next = next; } prev = next; Interval.State newState; if (cur.currentAtEnd()) { // move to handled state (not maintained as a list) newState = State.Handled; cur.state = newState; } else { if (cur.currentFrom() <= from) { // sort into active list activeLists.addToListSortedByCurrentFromPositions(binding, cur); newState = State.Active; } else { // sort into inactive list inactiveLists.addToListSortedByCurrentFromPositions(binding, cur); newState = State.Inactive; } cur.state = newState; if (prev == cur) { assert state == newState; prevprev = prev; prev = cur.next; } } intervalMoved(cur, state, newState); } else { prevprev = prev; prev = cur.next; } } } /** * Get the next interval from {@linkplain #unhandledLists} which starts before or at * {@code toOpId}. The returned interval is removed and {@link #currentBinding} is set. * * @postcondition all intervals in {@linkplain #unhandledLists} start after {@code toOpId}. * * @return The next interval or null if there is no {@linkplain #unhandledLists unhandled} * interval at position {@code toOpId}. */ private Interval nextInterval(int toOpId) { RegisterBinding binding; Interval any = unhandledLists.any; Interval fixed = unhandledLists.fixed; if (!any.isEndMarker()) { // intervals may start at same position . prefer fixed interval binding = !fixed.isEndMarker() && fixed.from() <= any.from() ? RegisterBinding.Fixed : RegisterBinding.Any; assert binding == RegisterBinding.Fixed && fixed.from() <= any.from() || binding == RegisterBinding.Any && any.from() <= fixed.from() : "wrong interval!!!"; assert any.isEndMarker() || fixed.isEndMarker() || any.from() != fixed.from() || binding == RegisterBinding.Fixed : "if fixed and any-Interval start at same position, fixed must be processed first"; } else if (!fixed.isEndMarker()) { binding = RegisterBinding.Fixed; } else { return null; } Interval currentInterval = unhandledLists.get(binding); if (toOpId < currentInterval.from()) { return null; } currentBinding = binding; unhandledLists.set(binding, currentInterval.next); currentInterval.next = allocator.intervalEndMarker; currentInterval.rewindRange(); return currentInterval; } /** * Walk up to {@code toOpId}. * * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeLists} and * {@link #inactiveLists} are populated and {@link Interval#state}s are up to * date. */ @SuppressWarnings("try") protected void walkTo(int toOpId) { assert currentPosition <= toOpId : "can not walk backwards"; for (Interval 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 walkTo(State.Active, opId); walkTo(State.Inactive, opId); try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) { currentInterval.state = State.Active; if (activateCurrent(currentInterval)) { activeLists.addToListSortedByCurrentFromPositions(currentBinding, 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 walkTo(State.Active, toOpId); walkTo(State.Inactive, toOpId); } } private void intervalMoved(Interval 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(allocator)); } } /** * Move {@linkplain #unhandledLists unhandled} stack intervals to * {@linkplain IntervalWalker #activeLists active}. * * Note that for {@linkplain RegisterBinding#Fixed fixed} and {@linkplain RegisterBinding#Any * any} intervals this is done in {@link #nextInterval(int)}. */ private void updateUnhandledStackIntervals(int opId) { Interval currentInterval = unhandledLists.get(RegisterBinding.Stack); while (!currentInterval.isEndMarker() && currentInterval.from() <= opId) { Interval next = currentInterval.next; if (currentInterval.to() > opId) { currentInterval.state = State.Active; activeLists.addToListSortedByCurrentFromPositions(RegisterBinding.Stack, currentInterval); intervalMoved(currentInterval, State.Unhandled, State.Active); } else { currentInterval.state = State.Handled; intervalMoved(currentInterval, State.Unhandled, State.Handled); } currentInterval = next; } unhandledLists.set(RegisterBinding.Stack, currentInterval); } }