Mercurial > hg > truffle
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) { |