001/*
002 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.lir.alloc.lsra;
024
025import com.oracle.graal.debug.*;
026
027import com.oracle.graal.lir.alloc.lsra.Interval.RegisterBinding;
028import com.oracle.graal.lir.alloc.lsra.Interval.RegisterBindingLists;
029import com.oracle.graal.lir.alloc.lsra.Interval.State;
030
031/**
032 */
033public class IntervalWalker {
034
035    protected final LinearScan allocator;
036
037    /**
038     * Sorted list of intervals, not live before the current position.
039     */
040    protected RegisterBindingLists unhandledLists;
041
042    /**
043     * Sorted list of intervals, live at the current position.
044     */
045    protected RegisterBindingLists activeLists;
046
047    /**
048     * Sorted list of intervals in a life time hole at the current position.
049     */
050    protected RegisterBindingLists inactiveLists;
051
052    /**
053     * The current position (intercept point through the intervals).
054     */
055    protected int currentPosition;
056
057    /**
058     * The binding of the current interval being processed.
059     */
060    protected RegisterBinding currentBinding;
061
062    /**
063     * Processes the {@code currentInterval} interval in an attempt to allocate a physical register
064     * to it and thus allow it to be moved to a list of {@linkplain #activeLists active} intervals.
065     *
066     * @return {@code true} if a register was allocated to the {@code currentInterval} interval
067     */
068    protected boolean activateCurrent(@SuppressWarnings({"unused"}) Interval currentInterval) {
069        return true;
070    }
071
072    void walkBefore(int lirOpId) {
073        walkTo(lirOpId - 1);
074    }
075
076    void walk() {
077        walkTo(Integer.MAX_VALUE);
078    }
079
080    /**
081     * Creates a new interval walker.
082     *
083     * @param allocator the register allocator context
084     * @param unhandledFixed the list of unhandled {@linkplain RegisterBinding#Fixed fixed}
085     *            intervals
086     * @param unhandledAny the list of unhandled {@linkplain RegisterBinding#Any non-fixed}
087     *            intervals
088     */
089    IntervalWalker(LinearScan allocator, Interval unhandledFixed, Interval unhandledAny) {
090        this.allocator = allocator;
091
092        unhandledLists = new RegisterBindingLists(unhandledFixed, unhandledAny, Interval.EndMarker);
093        activeLists = new RegisterBindingLists(Interval.EndMarker, Interval.EndMarker, Interval.EndMarker);
094        inactiveLists = new RegisterBindingLists(Interval.EndMarker, Interval.EndMarker, Interval.EndMarker);
095        currentPosition = -1;
096    }
097
098    protected void removeFromList(Interval interval) {
099        if (interval.state == State.Active) {
100            activeLists.remove(RegisterBinding.Any, interval);
101        } else {
102            assert interval.state == State.Inactive : "invalid state";
103            inactiveLists.remove(RegisterBinding.Any, interval);
104        }
105    }
106
107    private void walkTo(State state, int from) {
108        assert state == State.Active || state == State.Inactive : "wrong state";
109        for (RegisterBinding binding : RegisterBinding.VALUES) {
110            walkTo(state, from, binding);
111        }
112    }
113
114    private void walkTo(State state, int from, RegisterBinding binding) {
115        Interval prevprev = null;
116        Interval prev = (state == State.Active) ? activeLists.get(binding) : inactiveLists.get(binding);
117        Interval next = prev;
118        while (next.currentFrom() <= from) {
119            Interval cur = next;
120            next = cur.next;
121
122            boolean rangeHasChanged = false;
123            while (cur.currentTo() <= from) {
124                cur.nextRange();
125                rangeHasChanged = true;
126            }
127
128            // also handle move from inactive list to active list
129            rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from);
130
131            if (rangeHasChanged) {
132                // remove cur from list
133                if (prevprev == null) {
134                    if (state == State.Active) {
135                        activeLists.set(binding, next);
136                    } else {
137                        inactiveLists.set(binding, next);
138                    }
139                } else {
140                    prevprev.next = next;
141                }
142                prev = next;
143                Interval.State newState;
144                if (cur.currentAtEnd()) {
145                    // move to handled state (not maintained as a list)
146                    newState = State.Handled;
147                    cur.state = newState;
148                } else {
149                    if (cur.currentFrom() <= from) {
150                        // sort into active list
151                        activeLists.addToListSortedByCurrentFromPositions(binding, cur);
152                        newState = State.Active;
153                    } else {
154                        // sort into inactive list
155                        inactiveLists.addToListSortedByCurrentFromPositions(binding, cur);
156                        newState = State.Inactive;
157                    }
158                    cur.state = newState;
159                    if (prev == cur) {
160                        assert state == newState;
161                        prevprev = prev;
162                        prev = cur.next;
163                    }
164                }
165                intervalMoved(cur, state, newState);
166            } else {
167                prevprev = prev;
168                prev = cur.next;
169            }
170        }
171    }
172
173    /**
174     * Get the next interval from {@linkplain #unhandledLists} which starts before or at
175     * {@code toOpId}. The returned interval is removed and {@link #currentBinding} is set.
176     *
177     * @postcondition all intervals in {@linkplain #unhandledLists} start after {@code toOpId}.
178     *
179     * @return The next interval or null if there is no {@linkplain #unhandledLists unhandled}
180     *         interval at position {@code toOpId}.
181     */
182    private Interval nextInterval(int toOpId) {
183        RegisterBinding binding;
184        Interval any = unhandledLists.any;
185        Interval fixed = unhandledLists.fixed;
186
187        if (any != Interval.EndMarker) {
188            // intervals may start at same position . prefer fixed interval
189            binding = fixed != Interval.EndMarker && fixed.from() <= any.from() ? RegisterBinding.Fixed : RegisterBinding.Any;
190
191            assert binding == RegisterBinding.Fixed && fixed.from() <= any.from() || binding == RegisterBinding.Any && any.from() <= fixed.from() : "wrong interval!!!";
192            assert any == Interval.EndMarker || fixed == Interval.EndMarker || any.from() != fixed.from() || binding == RegisterBinding.Fixed : "if fixed and any-Interval start at same position, fixed must be processed first";
193
194        } else if (fixed != Interval.EndMarker) {
195            binding = RegisterBinding.Fixed;
196        } else {
197            return null;
198        }
199        Interval currentInterval = unhandledLists.get(binding);
200
201        if (toOpId < currentInterval.from()) {
202            return null;
203        }
204
205        currentBinding = binding;
206        unhandledLists.set(binding, currentInterval.next);
207        currentInterval.next = Interval.EndMarker;
208        currentInterval.rewindRange();
209        return currentInterval;
210    }
211
212    /**
213     * Walk up to {@code toOpId}.
214     *
215     * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeLists} and
216     *                {@link #inactiveLists} are populated and {@link Interval#state}s are up to
217     *                date.
218     */
219    protected void walkTo(int toOpId) {
220        assert currentPosition <= toOpId : "can not walk backwards";
221        for (Interval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) {
222            int opId = currentInterval.from();
223
224            // set currentPosition prior to call of walkTo
225            currentPosition = opId;
226
227            // update unhandled stack intervals
228            updateUnhandledStackIntervals(opId);
229
230            // call walkTo even if currentPosition == id
231            walkTo(State.Active, opId);
232            walkTo(State.Inactive, opId);
233
234            try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) {
235                currentInterval.state = State.Active;
236                if (activateCurrent(currentInterval)) {
237                    activeLists.addToListSortedByCurrentFromPositions(currentBinding, currentInterval);
238                    intervalMoved(currentInterval, State.Unhandled, State.Active);
239                }
240            }
241        }
242        // set currentPosition prior to call of walkTo
243        currentPosition = toOpId;
244
245        if (currentPosition <= allocator.maxOpId()) {
246            // update unhandled stack intervals
247            updateUnhandledStackIntervals(toOpId);
248
249            // call walkTo if still in range
250            walkTo(State.Active, toOpId);
251            walkTo(State.Inactive, toOpId);
252        }
253    }
254
255    private void intervalMoved(Interval interval, State from, State to) {
256        // intervalMoved() is called whenever an interval moves from one interval list to another.
257        // In the implementation of this method it is prohibited to move the interval to any list.
258        if (Debug.isLogEnabled()) {
259            Debug.log("interval moved from %s to %s: %s", from, to, interval.logString(allocator));
260        }
261    }
262
263    /**
264     * Move {@linkplain #unhandledLists unhandled} stack intervals to
265     * {@linkplain IntervalWalker #activeLists active}.
266     *
267     * Note that for {@linkplain RegisterBinding#Fixed fixed} and {@linkplain RegisterBinding#Any
268     * any} intervals this is done in {@link #nextInterval(int)}.
269     */
270    private void updateUnhandledStackIntervals(int opId) {
271        Interval currentInterval = unhandledLists.get(RegisterBinding.Stack);
272        while (currentInterval != Interval.EndMarker && currentInterval.from() <= opId) {
273            Interval next = currentInterval.next;
274            if (currentInterval.to() > opId) {
275                currentInterval.state = State.Active;
276                activeLists.addToListSortedByCurrentFromPositions(RegisterBinding.Stack, currentInterval);
277                intervalMoved(currentInterval, State.Unhandled, State.Active);
278            } else {
279                currentInterval.state = State.Handled;
280                intervalMoved(currentInterval, State.Unhandled, State.Handled);
281            }
282            currentInterval = next;
283        }
284        unhandledLists.set(RegisterBinding.Stack, currentInterval);
285    }
286
287}