1 /*
2  * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.lir.alloc.lsra;
26 
27 import org.graalvm.compiler.debug.DebugContext;
28 import org.graalvm.compiler.debug.Indent;
29 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBinding;
30 import org.graalvm.compiler.lir.alloc.lsra.Interval.RegisterBindingLists;
31 import org.graalvm.compiler.lir.alloc.lsra.Interval.State;
32 
33 /**
34  */
35 public class IntervalWalker {
36 
37     protected final LinearScan allocator;
38 
39     /**
40      * Sorted list of intervals, not live before the current position.
41      */
42     protected RegisterBindingLists unhandledLists;
43 
44     /**
45      * Sorted list of intervals, live at the current position.
46      */
47     protected RegisterBindingLists activeLists;
48 
49     /**
50      * Sorted list of intervals in a life time hole at the current position.
51      */
52     protected RegisterBindingLists inactiveLists;
53 
54     /**
55      * The current position (intercept point through the intervals).
56      */
57     protected int currentPosition;
58 
59     /**
60      * The binding of the current interval being processed.
61      */
62     protected RegisterBinding currentBinding;
63 
64     /**
65      * Processes the {@code currentInterval} interval in an attempt to allocate a physical register
66      * to it and thus allow it to be moved to a list of {@linkplain #activeLists active} intervals.
67      *
68      * @return {@code true} if a register was allocated to the {@code currentInterval} interval
69      */
activateCurrent(@uppressWarnings{R}) Interval currentInterval)70     protected boolean activateCurrent(@SuppressWarnings({"unused"}) Interval currentInterval) {
71         return true;
72     }
73 
walkBefore(int lirOpId)74     void walkBefore(int lirOpId) {
75         walkTo(lirOpId - 1);
76     }
77 
walk()78     void walk() {
79         walkTo(Integer.MAX_VALUE);
80     }
81 
82     /**
83      * Creates a new interval walker.
84      *
85      * @param allocator the register allocator context
86      * @param unhandledFixed the list of unhandled {@linkplain RegisterBinding#Fixed fixed}
87      *            intervals
88      * @param unhandledAny the list of unhandled {@linkplain RegisterBinding#Any non-fixed}
89      *            intervals
90      */
IntervalWalker(LinearScan allocator, Interval unhandledFixed, Interval unhandledAny)91     IntervalWalker(LinearScan allocator, Interval unhandledFixed, Interval unhandledAny) {
92         this.allocator = allocator;
93 
94         unhandledLists = new RegisterBindingLists(unhandledFixed, unhandledAny, allocator.intervalEndMarker);
95         activeLists = new RegisterBindingLists(allocator.intervalEndMarker, allocator.intervalEndMarker, allocator.intervalEndMarker);
96         inactiveLists = new RegisterBindingLists(allocator.intervalEndMarker, allocator.intervalEndMarker, allocator.intervalEndMarker);
97         currentPosition = -1;
98     }
99 
removeFromList(Interval interval)100     protected void removeFromList(Interval interval) {
101         if (interval.state == State.Active) {
102             activeLists.remove(RegisterBinding.Any, interval);
103         } else {
104             assert interval.state == State.Inactive : "invalid state";
105             inactiveLists.remove(RegisterBinding.Any, interval);
106         }
107     }
108 
walkTo(State state, int from)109     private void walkTo(State state, int from) {
110         assert state == State.Active || state == State.Inactive : "wrong state";
111         for (RegisterBinding binding : RegisterBinding.VALUES) {
112             walkTo(state, from, binding);
113         }
114     }
115 
walkTo(State state, int from, RegisterBinding binding)116     private void walkTo(State state, int from, RegisterBinding binding) {
117         Interval prevprev = null;
118         Interval prev = (state == State.Active) ? activeLists.get(binding) : inactiveLists.get(binding);
119         Interval next = prev;
120         while (next.currentFrom() <= from) {
121             Interval cur = next;
122             next = cur.next;
123 
124             boolean rangeHasChanged = false;
125             while (cur.currentTo() <= from) {
126                 cur.nextRange();
127                 rangeHasChanged = true;
128             }
129 
130             // also handle move from inactive list to active list
131             rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from);
132 
133             if (rangeHasChanged) {
134                 // remove cur from list
135                 if (prevprev == null) {
136                     if (state == State.Active) {
137                         activeLists.set(binding, next);
138                     } else {
139                         inactiveLists.set(binding, next);
140                     }
141                 } else {
142                     prevprev.next = next;
143                 }
144                 prev = next;
145                 Interval.State newState;
146                 if (cur.currentAtEnd()) {
147                     // move to handled state (not maintained as a list)
148                     newState = State.Handled;
149                     cur.state = newState;
150                 } else {
151                     if (cur.currentFrom() <= from) {
152                         // sort into active list
153                         activeLists.addToListSortedByCurrentFromPositions(binding, cur);
154                         newState = State.Active;
155                     } else {
156                         // sort into inactive list
157                         inactiveLists.addToListSortedByCurrentFromPositions(binding, cur);
158                         newState = State.Inactive;
159                     }
160                     cur.state = newState;
161                     if (prev == cur) {
162                         assert state == newState;
163                         prevprev = prev;
164                         prev = cur.next;
165                     }
166                 }
167                 intervalMoved(cur, state, newState);
168             } else {
169                 prevprev = prev;
170                 prev = cur.next;
171             }
172         }
173     }
174 
175     /**
176      * Get the next interval from {@linkplain #unhandledLists} which starts before or at
177      * {@code toOpId}. The returned interval is removed and {@link #currentBinding} is set.
178      *
179      * @postcondition all intervals in {@linkplain #unhandledLists} start after {@code toOpId}.
180      *
181      * @return The next interval or null if there is no {@linkplain #unhandledLists unhandled}
182      *         interval at position {@code toOpId}.
183      */
nextInterval(int toOpId)184     private Interval nextInterval(int toOpId) {
185         RegisterBinding binding;
186         Interval any = unhandledLists.any;
187         Interval fixed = unhandledLists.fixed;
188 
189         if (!any.isEndMarker()) {
190             // intervals may start at same position . prefer fixed interval
191             binding = !fixed.isEndMarker() && fixed.from() <= any.from() ? RegisterBinding.Fixed : RegisterBinding.Any;
192 
193             assert binding == RegisterBinding.Fixed && fixed.from() <= any.from() || binding == RegisterBinding.Any && any.from() <= fixed.from() : "wrong interval!!!";
194             assert any.isEndMarker() || fixed.isEndMarker() || any.from() != fixed.from() ||
195                             binding == RegisterBinding.Fixed : "if fixed and any-Interval start at same position, fixed must be processed first";
196 
197         } else if (!fixed.isEndMarker()) {
198             binding = RegisterBinding.Fixed;
199         } else {
200             return null;
201         }
202         Interval currentInterval = unhandledLists.get(binding);
203 
204         if (toOpId < currentInterval.from()) {
205             return null;
206         }
207 
208         currentBinding = binding;
209         unhandledLists.set(binding, currentInterval.next);
210         currentInterval.next = allocator.intervalEndMarker;
211         currentInterval.rewindRange();
212         return currentInterval;
213     }
214 
215     /**
216      * Walk up to {@code toOpId}.
217      *
218      * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeLists} and
219      *                {@link #inactiveLists} are populated and {@link Interval#state}s are up to
220      *                date.
221      */
222     @SuppressWarnings("try")
walkTo(int toOpId)223     protected void walkTo(int toOpId) {
224         assert currentPosition <= toOpId : "can not walk backwards";
225         for (Interval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) {
226             int opId = currentInterval.from();
227 
228             // set currentPosition prior to call of walkTo
229             currentPosition = opId;
230 
231             // update unhandled stack intervals
232             updateUnhandledStackIntervals(opId);
233 
234             // call walkTo even if currentPosition == id
235             walkTo(State.Active, opId);
236             walkTo(State.Inactive, opId);
237 
238             DebugContext debug = allocator.getDebug();
239             try (Indent indent = debug.logAndIndent("walk to op %d", opId)) {
240                 currentInterval.state = State.Active;
241                 if (activateCurrent(currentInterval)) {
242                     activeLists.addToListSortedByCurrentFromPositions(currentBinding, currentInterval);
243                     intervalMoved(currentInterval, State.Unhandled, State.Active);
244                 }
245             }
246         }
247         // set currentPosition prior to call of walkTo
248         currentPosition = toOpId;
249 
250         if (currentPosition <= allocator.maxOpId()) {
251             // update unhandled stack intervals
252             updateUnhandledStackIntervals(toOpId);
253 
254             // call walkTo if still in range
255             walkTo(State.Active, toOpId);
256             walkTo(State.Inactive, toOpId);
257         }
258     }
259 
intervalMoved(Interval interval, State from, State to)260     private void intervalMoved(Interval interval, State from, State to) {
261         // intervalMoved() is called whenever an interval moves from one interval list to another.
262         // In the implementation of this method it is prohibited to move the interval to any list.
263         DebugContext debug = allocator.getDebug();
264         if (debug.isLogEnabled()) {
265             debug.log("interval moved from %s to %s: %s", from, to, interval.logString(allocator));
266         }
267     }
268 
269     /**
270      * Move {@linkplain #unhandledLists unhandled} stack intervals to
271      * {@linkplain IntervalWalker #activeLists active}.
272      *
273      * Note that for {@linkplain RegisterBinding#Fixed fixed} and {@linkplain RegisterBinding#Any
274      * any} intervals this is done in {@link #nextInterval(int)}.
275      */
updateUnhandledStackIntervals(int opId)276     private void updateUnhandledStackIntervals(int opId) {
277         Interval currentInterval = unhandledLists.get(RegisterBinding.Stack);
278         while (!currentInterval.isEndMarker() && currentInterval.from() <= opId) {
279             Interval next = currentInterval.next;
280             if (currentInterval.to() > opId) {
281                 currentInterval.state = State.Active;
282                 activeLists.addToListSortedByCurrentFromPositions(RegisterBinding.Stack, currentInterval);
283                 intervalMoved(currentInterval, State.Unhandled, State.Active);
284             } else {
285                 currentInterval.state = State.Handled;
286                 intervalMoved(currentInterval, State.Unhandled, State.Handled);
287             }
288             currentInterval = next;
289         }
290         unhandledLists.set(RegisterBinding.Stack, currentInterval);
291     }
292 
293 }
294