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