comparison graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java @ 13302:094f4ee93977

some javadoc for switch strategies
author Lukas Stadler <lukas.stadler@jku.at>
date Thu, 12 Dec 2013 18:15:22 +0100
parents d5e65a244f7d
children da0851712519
comparison
equal deleted inserted replaced
13301:126ef8e8aa59 13302:094f4ee93977
27 import com.oracle.graal.api.meta.*; 27 import com.oracle.graal.api.meta.*;
28 import com.oracle.graal.asm.*; 28 import com.oracle.graal.asm.*;
29 import com.oracle.graal.lir.asm.*; 29 import com.oracle.graal.lir.asm.*;
30 import com.oracle.graal.nodes.calc.*; 30 import com.oracle.graal.nodes.calc.*;
31 31
32 /**
33 * This class encapsulates different strategies on how to generate code for switch instructions.
34 *
35 * The {@link #getBestStrategy(double[], Constant[], LabelRef[])} method can be used to get strategy
36 * with the smallest average effort (average number of comparisons until a decision is reached). The
37 * strategy returned by this method will have its averageEffort set, while a strategy constructed
38 * directly will not.
39 */
32 public abstract class SwitchStrategy { 40 public abstract class SwitchStrategy {
33 41
34 private interface SwitchClosure { 42 private interface SwitchClosure {
35 /** 43 /**
36 * Generates a conditional or unconditional jump. The jump will be unconditional if 44 * Generates a conditional or unconditional jump. The jump will be unconditional if
73 */ 81 */
74 boolean isSameTarget(int index1, int index2); 82 boolean isSameTarget(int index1, int index2);
75 } 83 }
76 84
77 /** 85 /**
78 * Backends can subclass this abstract class to generate code for switch strategies. 86 * Backends can subclass this abstract class and generate code for switch strategies by
87 * implementing the {@link #conditionalJump(int, Condition, Label)} method.
79 */ 88 */
80 public abstract static class BaseSwitchClosure implements SwitchClosure { 89 public abstract static class BaseSwitchClosure implements SwitchClosure {
81 90
82 private final CompilationResultBuilder crb; 91 private final CompilationResultBuilder crb;
83 private final AbstractAssembler masm; 92 private final AbstractAssembler masm;
131 return keyTargets[index1] == keyTargets[index2]; 140 return keyTargets[index1] == keyTargets[index2];
132 } 141 }
133 142
134 } 143 }
135 144
145 /**
146 * This closure is used internally to determine the average effort for a certain strategy on a
147 * given switch instruction.
148 */
136 private class EffortClosure implements SwitchClosure { 149 private class EffortClosure implements SwitchClosure {
137 150
138 private int defaultEffort; 151 private int defaultEffort;
139 private int defaultCount; 152 private int defaultCount;
140 private final int[] keyEfforts = new int[keyConstants.length]; 153 private final int[] keyEfforts = new int[keyConstants.length];
187 this.keyProbabilities = keyProbabilities; 200 this.keyProbabilities = keyProbabilities;
188 this.keyConstants = keyConstants; 201 this.keyConstants = keyConstants;
189 } 202 }
190 203
191 public double getAverageEffort() { 204 public double getAverageEffort() {
192 assert averageEffort >= 0; 205 assert averageEffort >= 0 : "average effort was not calculated yet for this strategy";
193 return averageEffort; 206 return averageEffort;
194 } 207 }
195 208
196 /* 209 /**
197 * Looks for the end of a stretch of key constants that are successive numbers and have the same 210 * Looks for the end of a stretch of key constants that are successive numbers and have the same
198 * target. 211 * target.
199 */ 212 */
200 protected int getSliceEnd(SwitchClosure closure, int pos) { 213 protected int getSliceEnd(SwitchClosure closure, int pos) {
201 int slice = pos; 214 int slice = pos;
203 slice++; 216 slice++;
204 } 217 }
205 return slice; 218 return slice;
206 } 219 }
207 220
221 /**
222 * Tells the system that the given (inclusive) range of keys is reached after depth number of
223 * comparisons, which is used to calculate the average effort.
224 */
208 protected void registerEffort(int rangeStart, int rangeEnd, int depth) { 225 protected void registerEffort(int rangeStart, int rangeEnd, int depth) {
209 if (effortClosure != null) { 226 if (effortClosure != null) {
210 for (int i = rangeStart; i <= rangeEnd; i++) { 227 for (int i = rangeStart; i <= rangeEnd; i++) {
211 effortClosure.keyEfforts[i] += depth; 228 effortClosure.keyEfforts[i] += depth;
212 effortClosure.keyCounts[i]++; 229 effortClosure.keyCounts[i]++;
213 } 230 }
214 } 231 }
215 } 232 }
216 233
234 /**
235 * Tells the system that the default successor is reached after depth number of comparisons,
236 * which is used to calculate average effort.
237 */
217 protected void registerDefaultEffort(int depth) { 238 protected void registerDefaultEffort(int depth) {
218 if (effortClosure != null) { 239 if (effortClosure != null) {
219 effortClosure.defaultEffort += depth; 240 effortClosure.defaultEffort += depth;
220 effortClosure.defaultCount++; 241 effortClosure.defaultCount++;
221 } 242 }
224 @Override 245 @Override
225 public String toString() { 246 public String toString() {
226 return getClass().getSimpleName() + "[avgEffort=" + averageEffort + "]"; 247 return getClass().getSimpleName() + "[avgEffort=" + averageEffort + "]";
227 } 248 }
228 249
250 /**
251 * This strategy orders the keys according to their probability and creates one equality
252 * comparison per key.
253 */
229 public static class SequentialStrategy extends SwitchStrategy { 254 public static class SequentialStrategy extends SwitchStrategy {
230 private final Integer[] indexes; 255 private final Integer[] indexes;
231 256
232 public SequentialStrategy(final double[] keyProbabilities, Constant[] keyConstants) { 257 public SequentialStrategy(final double[] keyProbabilities, Constant[] keyConstants) {
233 super(keyProbabilities, keyConstants); 258 super(keyProbabilities, keyConstants);
255 registerEffort(indexes[keyConstants.length - 1], indexes[keyConstants.length - 1], keyConstants.length); 280 registerEffort(indexes[keyConstants.length - 1], indexes[keyConstants.length - 1], keyConstants.length);
256 registerDefaultEffort(keyConstants.length); 281 registerDefaultEffort(keyConstants.length);
257 } 282 }
258 } 283 }
259 284
285 /**
286 * This strategy divides the keys into ranges of successive keys with the same target and
287 * creates comparisons for these ranges.
288 */
260 public static class RangesStrategy extends SwitchStrategy { 289 public static class RangesStrategy extends SwitchStrategy {
261 private final Integer[] indexes; 290 private final Integer[] indexes;
262 291
263 public RangesStrategy(final double[] keyProbabilities, Constant[] keyConstants) { 292 public RangesStrategy(final double[] keyProbabilities, Constant[] keyConstants) {
264 super(keyProbabilities, keyConstants); 293 super(keyProbabilities, keyConstants);
312 registerDefaultEffort(depth); 341 registerDefaultEffort(depth);
313 } 342 }
314 } 343 }
315 } 344 }
316 345
346 /**
347 * This strategy recursively subdivides the list of keys to create a binary search based on
348 * probabilities.
349 */
317 public static class BinaryStrategy extends SwitchStrategy { 350 public static class BinaryStrategy extends SwitchStrategy {
318 351
319 private static final double MIN_PROBABILITY = 0.00001; 352 private static final double MIN_PROBABILITY = 0.00001;
320 353
321 private final double[] probabilitySums; 354 private final double[] probabilitySums;
334 public void run(SwitchClosure closure) { 367 public void run(SwitchClosure closure) {
335 recurseBinarySwitch(closure, 0, keyConstants.length - 1, 0); 368 recurseBinarySwitch(closure, 0, keyConstants.length - 1, 0);
336 } 369 }
337 370
338 /** 371 /**
339 * Recursively generate a list of comparisons that always subdivides the key list in the 372 * Recursively generate a list of comparisons that always subdivides the keys in the given
340 * middle (in terms of probability, not index). 373 * (inclusive) range in the middle (in terms of probability, not index). If left is bigger
374 * than zero, then we always know that the value is equal to or bigger than the left key.
375 * This does not hold for the right key, as there may be a gap afterwards.
341 */ 376 */
342 private void recurseBinarySwitch(SwitchClosure closure, int left, int right, int startDepth) { 377 private void recurseBinarySwitch(SwitchClosure closure, int left, int right, int startDepth) {
343 assert startDepth < keyConstants.length * 3 : "runaway recursion in binary switch"; 378 assert startDepth < keyConstants.length * 3 : "runaway recursion in binary switch";
344 int depth = startDepth; 379 int depth = startDepth;
345 boolean leftBorder = left == 0; 380 boolean leftBorder = left == 0;
352 registerEffort(left, left, ++depth); 387 registerEffort(left, left, ++depth);
353 closure.conditionalJumpOrDefault(right, Condition.EQ, rightBorder); 388 closure.conditionalJumpOrDefault(right, Condition.EQ, rightBorder);
354 registerEffort(right, right, ++depth); 389 registerEffort(right, right, ++depth);
355 registerDefaultEffort(depth); 390 registerDefaultEffort(depth);
356 } else { 391 } else {
392 // here we know that the value can only be one of these two keys in the range
357 closure.conditionalJump(left, Condition.EQ, false); 393 closure.conditionalJump(left, Condition.EQ, false);
358 registerEffort(left, left, ++depth); 394 registerEffort(left, left, ++depth);
359 closure.conditionalJump(right, null, false); 395 closure.conditionalJump(right, null, false);
360 registerEffort(right, right, depth); 396 registerEffort(right, right, depth);
361 } 397 }
431 strategy.effortClosure = null; 467 strategy.effortClosure = null;
432 } 468 }
433 return strategies; 469 return strategies;
434 } 470 }
435 471
472 /**
473 * Creates all switch strategies for the given switch, evaluates them (based on average effort)
474 * and returns the best one.
475 */
436 public static SwitchStrategy getBestStrategy(double[] keyProbabilities, Constant[] keyConstants, LabelRef[] keyTargets) { 476 public static SwitchStrategy getBestStrategy(double[] keyProbabilities, Constant[] keyConstants, LabelRef[] keyTargets) {
437 SwitchStrategy[] strategies = getStrategies(keyProbabilities, keyConstants, keyTargets); 477 SwitchStrategy[] strategies = getStrategies(keyProbabilities, keyConstants, keyTargets);
438 double bestEffort = Integer.MAX_VALUE; 478 double bestEffort = Integer.MAX_VALUE;
439 SwitchStrategy bestStrategy = null; 479 SwitchStrategy bestStrategy = null;
440 for (SwitchStrategy strategy : strategies) { 480 for (SwitchStrategy strategy : strategies) {