001/*
002 * Copyright (c) 2013, 2014, 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;
024
025import java.util.*;
026
027import jdk.internal.jvmci.meta.*;
028
029import com.oracle.graal.asm.*;
030import com.oracle.graal.compiler.common.calc.*;
031import com.oracle.graal.lir.asm.*;
032
033/**
034 * This class encapsulates different strategies on how to generate code for switch instructions.
035 *
036 * The {@link #getBestStrategy(double[], JavaConstant[], LabelRef[])} method can be used to get
037 * strategy with the smallest average effort (average number of comparisons until a decision is
038 * reached). The strategy returned by this method will have its averageEffort set, while a strategy
039 * constructed directly will not.
040 */
041public abstract class SwitchStrategy {
042
043    private interface SwitchClosure {
044        /**
045         * Generates a conditional or unconditional jump. The jump will be unconditional if
046         * condition is null. If defaultTarget is true, then the jump will go the the default.
047         *
048         * @param index Index of the value and the jump target (only used if defaultTarget == false)
049         * @param condition The condition on which to jump (can be null)
050         * @param defaultTarget true if the jump should go to the default target, false if index
051         *            should be used.
052         */
053        void conditionalJump(int index, Condition condition, boolean defaultTarget);
054
055        /**
056         * Generates a conditional jump to the target with the specified index. The fall through
057         * should go to the default target.
058         *
059         * @param index Index of the value and the jump target
060         * @param condition The condition on which to jump
061         * @param canFallThrough true if this is the last instruction in the switch statement, to
062         *            allow for fall-through optimizations.
063         */
064        void conditionalJumpOrDefault(int index, Condition condition, boolean canFallThrough);
065
066        /**
067         * Create a new label and generate a conditional jump to it.
068         *
069         * @param index Index of the value and the jump target
070         * @param condition The condition on which to jump
071         * @return a new Label
072         */
073        Label conditionalJump(int index, Condition condition);
074
075        /**
076         * Binds a label returned by {@link #conditionalJump(int, Condition)}.
077         */
078        void bind(Label label);
079
080        /**
081         * Return true iff the target of both indexes is the same.
082         */
083        boolean isSameTarget(int index1, int index2);
084    }
085
086    /**
087     * Backends can subclass this abstract class and generate code for switch strategies by
088     * implementing the {@link #conditionalJump(int, Condition, Label)} method.
089     */
090    public abstract static class BaseSwitchClosure implements SwitchClosure {
091
092        private final CompilationResultBuilder crb;
093        private final Assembler masm;
094        private final LabelRef[] keyTargets;
095        private final LabelRef defaultTarget;
096
097        public BaseSwitchClosure(CompilationResultBuilder crb, Assembler masm, LabelRef[] keyTargets, LabelRef defaultTarget) {
098            this.crb = crb;
099            this.masm = masm;
100            this.keyTargets = keyTargets;
101            this.defaultTarget = defaultTarget;
102        }
103
104        /**
105         * This method generates code for a comparison between the actual value and the constant at
106         * the given index and a condition jump to target.
107         */
108        protected abstract void conditionalJump(int index, Condition condition, Label target);
109
110        public void conditionalJump(int index, Condition condition, boolean targetDefault) {
111            Label target = targetDefault ? defaultTarget.label() : keyTargets[index].label();
112            if (condition == null) {
113                masm.jmp(target);
114            } else {
115                conditionalJump(index, condition, target);
116            }
117        }
118
119        public void conditionalJumpOrDefault(int index, Condition condition, boolean canFallThrough) {
120            if (canFallThrough && crb.isSuccessorEdge(defaultTarget)) {
121                conditionalJump(index, condition, keyTargets[index].label());
122            } else if (canFallThrough && crb.isSuccessorEdge(keyTargets[index])) {
123                conditionalJump(index, condition.negate(), defaultTarget.label());
124            } else {
125                conditionalJump(index, condition, keyTargets[index].label());
126                masm.jmp(defaultTarget.label());
127            }
128        }
129
130        public Label conditionalJump(int index, Condition condition) {
131            Label label = new Label();
132            conditionalJump(index, condition, label);
133            return label;
134        }
135
136        public void bind(Label label) {
137            masm.bind(label);
138        }
139
140        public boolean isSameTarget(int index1, int index2) {
141            return keyTargets[index1] == keyTargets[index2];
142        }
143
144    }
145
146    /**
147     * This closure is used internally to determine the average effort for a certain strategy on a
148     * given switch instruction.
149     */
150    private class EffortClosure implements SwitchClosure {
151
152        private int defaultEffort;
153        private int defaultCount;
154        private final int[] keyEfforts = new int[keyConstants.length];
155        private final int[] keyCounts = new int[keyConstants.length];
156        private final LabelRef[] keyTargets;
157
158        public EffortClosure(LabelRef[] keyTargets) {
159            this.keyTargets = keyTargets;
160        }
161
162        public void conditionalJump(int index, Condition condition, boolean defaultTarget) {
163            // nothing to do
164        }
165
166        public void conditionalJumpOrDefault(int index, Condition condition, boolean canFallThrough) {
167            // nothing to do
168        }
169
170        public Label conditionalJump(int index, Condition condition) {
171            // nothing to do
172            return null;
173        }
174
175        public void bind(Label label) {
176            // nothing to do
177        }
178
179        public boolean isSameTarget(int index1, int index2) {
180            return keyTargets[index1] == keyTargets[index2];
181        }
182
183        public double getAverageEffort() {
184            double defaultProbability = 1;
185            double effort = 0;
186            for (int i = 0; i < keyConstants.length; i++) {
187                effort += keyEfforts[i] * keyProbabilities[i] / keyCounts[i];
188                defaultProbability -= keyProbabilities[i];
189            }
190            return effort + defaultEffort * defaultProbability / defaultCount;
191        }
192    }
193
194    public final double[] keyProbabilities;
195    public final JavaConstant[] keyConstants;
196    private double averageEffort = -1;
197    private EffortClosure effortClosure;
198
199    public SwitchStrategy(double[] keyProbabilities, JavaConstant[] keyConstants) {
200        assert keyConstants.length == keyProbabilities.length && keyConstants.length >= 2;
201        this.keyProbabilities = keyProbabilities;
202        this.keyConstants = keyConstants;
203    }
204
205    public double getAverageEffort() {
206        assert averageEffort >= 0 : "average effort was not calculated yet for this strategy";
207        return averageEffort;
208    }
209
210    /**
211     * Looks for the end of a stretch of key constants that are successive numbers and have the same
212     * target.
213     */
214    protected int getSliceEnd(SwitchClosure closure, int pos) {
215        int slice = pos;
216        while (slice < (keyConstants.length - 1) && keyConstants[slice + 1].asLong() == keyConstants[slice].asLong() + 1 && closure.isSameTarget(slice, slice + 1)) {
217            slice++;
218        }
219        return slice;
220    }
221
222    /**
223     * Tells the system that the given (inclusive) range of keys is reached after depth number of
224     * comparisons, which is used to calculate the average effort.
225     */
226    protected void registerEffort(int rangeStart, int rangeEnd, int depth) {
227        if (effortClosure != null) {
228            for (int i = rangeStart; i <= rangeEnd; i++) {
229                effortClosure.keyEfforts[i] += depth;
230                effortClosure.keyCounts[i]++;
231            }
232        }
233    }
234
235    /**
236     * Tells the system that the default successor is reached after depth number of comparisons,
237     * which is used to calculate average effort.
238     */
239    protected void registerDefaultEffort(int depth) {
240        if (effortClosure != null) {
241            effortClosure.defaultEffort += depth;
242            effortClosure.defaultCount++;
243        }
244    }
245
246    @Override
247    public String toString() {
248        return getClass().getSimpleName() + "[avgEffort=" + averageEffort + "]";
249    }
250
251    /**
252     * This strategy orders the keys according to their probability and creates one equality
253     * comparison per key.
254     */
255    public static class SequentialStrategy extends SwitchStrategy {
256        private final Integer[] indexes;
257
258        public SequentialStrategy(final double[] keyProbabilities, JavaConstant[] keyConstants) {
259            super(keyProbabilities, keyConstants);
260
261            int keyCount = keyConstants.length;
262            indexes = new Integer[keyCount];
263            for (int i = 0; i < keyCount; i++) {
264                indexes[i] = i;
265            }
266            Arrays.sort(indexes, new Comparator<Integer>() {
267                @Override
268                public int compare(Integer o1, Integer o2) {
269                    return keyProbabilities[o1] < keyProbabilities[o2] ? 1 : keyProbabilities[o1] > keyProbabilities[o2] ? -1 : 0;
270                }
271            });
272        }
273
274        @Override
275        public void run(SwitchClosure closure) {
276            for (int i = 0; i < keyConstants.length - 1; i++) {
277                closure.conditionalJump(indexes[i], Condition.EQ, false);
278                registerEffort(indexes[i], indexes[i], i + 1);
279            }
280            closure.conditionalJumpOrDefault(indexes[keyConstants.length - 1], Condition.EQ, true);
281            registerEffort(indexes[keyConstants.length - 1], indexes[keyConstants.length - 1], keyConstants.length);
282            registerDefaultEffort(keyConstants.length);
283        }
284    }
285
286    /**
287     * This strategy divides the keys into ranges of successive keys with the same target and
288     * creates comparisons for these ranges.
289     */
290    public static class RangesStrategy extends SwitchStrategy {
291        private final Integer[] indexes;
292
293        public RangesStrategy(final double[] keyProbabilities, JavaConstant[] keyConstants) {
294            super(keyProbabilities, keyConstants);
295
296            int keyCount = keyConstants.length;
297            indexes = new Integer[keyCount];
298            for (int i = 0; i < keyCount; i++) {
299                indexes[i] = i;
300            }
301            Arrays.sort(indexes, new Comparator<Integer>() {
302                @Override
303                public int compare(Integer o1, Integer o2) {
304                    return keyProbabilities[o1] < keyProbabilities[o2] ? 1 : keyProbabilities[o1] > keyProbabilities[o2] ? -1 : 0;
305                }
306            });
307        }
308
309        @Override
310        public void run(SwitchClosure closure) {
311            int depth = 0;
312            closure.conditionalJump(0, Condition.LT, true);
313            registerDefaultEffort(++depth);
314            int rangeStart = 0;
315            int rangeEnd = getSliceEnd(closure, rangeStart);
316            while (rangeEnd != keyConstants.length - 1) {
317                if (rangeStart == rangeEnd) {
318                    closure.conditionalJump(rangeStart, Condition.EQ, false);
319                    registerEffort(rangeStart, rangeEnd, ++depth);
320                } else {
321                    if (rangeStart == 0 || keyConstants[rangeStart - 1].asLong() + 1 != keyConstants[rangeStart].asLong()) {
322                        closure.conditionalJump(rangeStart, Condition.LT, true);
323                        registerDefaultEffort(++depth);
324                    }
325                    closure.conditionalJump(rangeEnd, Condition.LE, false);
326                    registerEffort(rangeStart, rangeEnd, ++depth);
327                }
328                rangeStart = rangeEnd + 1;
329                rangeEnd = getSliceEnd(closure, rangeStart);
330            }
331            if (rangeStart == rangeEnd) {
332                closure.conditionalJumpOrDefault(rangeStart, Condition.EQ, true);
333                registerEffort(rangeStart, rangeEnd, ++depth);
334                registerDefaultEffort(depth);
335            } else {
336                if (rangeStart == 0 || keyConstants[rangeStart - 1].asLong() + 1 != keyConstants[rangeStart].asLong()) {
337                    closure.conditionalJump(rangeStart, Condition.LT, true);
338                    registerDefaultEffort(++depth);
339                }
340                closure.conditionalJumpOrDefault(rangeEnd, Condition.LE, true);
341                registerEffort(rangeStart, rangeEnd, ++depth);
342                registerDefaultEffort(depth);
343            }
344        }
345    }
346
347    /**
348     * This strategy recursively subdivides the list of keys to create a binary search based on
349     * probabilities.
350     */
351    public static class BinaryStrategy extends SwitchStrategy {
352
353        private static final double MIN_PROBABILITY = 0.00001;
354
355        private final double[] probabilitySums;
356
357        public BinaryStrategy(double[] keyProbabilities, JavaConstant[] keyConstants) {
358            super(keyProbabilities, keyConstants);
359            probabilitySums = new double[keyProbabilities.length + 1];
360            double sum = 0;
361            for (int i = 0; i < keyConstants.length; i++) {
362                sum += Math.max(keyProbabilities[i], MIN_PROBABILITY);
363                probabilitySums[i + 1] = sum;
364            }
365        }
366
367        @Override
368        public void run(SwitchClosure closure) {
369            recurseBinarySwitch(closure, 0, keyConstants.length - 1, 0);
370        }
371
372        /**
373         * Recursively generate a list of comparisons that always subdivides the keys in the given
374         * (inclusive) range in the middle (in terms of probability, not index). If left is bigger
375         * than zero, then we always know that the value is equal to or bigger than the left key.
376         * This does not hold for the right key, as there may be a gap afterwards.
377         */
378        private void recurseBinarySwitch(SwitchClosure closure, int left, int right, int startDepth) {
379            assert startDepth < keyConstants.length * 3 : "runaway recursion in binary switch";
380            int depth = startDepth;
381            boolean leftBorder = left == 0;
382            boolean rightBorder = right == keyConstants.length - 1;
383
384            if (left + 1 == right) {
385                // only two possible values
386                if (leftBorder || rightBorder || keyConstants[right].asLong() + 1 != keyConstants[right + 1].asLong() || keyConstants[left].asLong() + 1 != keyConstants[right].asLong()) {
387                    closure.conditionalJump(left, Condition.EQ, false);
388                    registerEffort(left, left, ++depth);
389                    closure.conditionalJumpOrDefault(right, Condition.EQ, rightBorder);
390                    registerEffort(right, right, ++depth);
391                    registerDefaultEffort(depth);
392                } else {
393                    // here we know that the value can only be one of these two keys in the range
394                    closure.conditionalJump(left, Condition.EQ, false);
395                    registerEffort(left, left, ++depth);
396                    closure.conditionalJump(right, null, false);
397                    registerEffort(right, right, depth);
398                }
399                return;
400            }
401            double probabilityStart = probabilitySums[left];
402            double probabilityMiddle = (probabilityStart + probabilitySums[right + 1]) / 2;
403            assert probabilityStart >= probabilityStart;
404            int middle = left;
405            while (getSliceEnd(closure, middle + 1) < right && probabilitySums[getSliceEnd(closure, middle + 1)] < probabilityMiddle) {
406                middle = getSliceEnd(closure, middle + 1);
407            }
408            middle = getSliceEnd(closure, middle);
409            assert middle < keyConstants.length - 1;
410
411            if (getSliceEnd(closure, left) == middle) {
412                if (left == 0) {
413                    closure.conditionalJump(0, Condition.LT, true);
414                    registerDefaultEffort(++depth);
415                }
416                closure.conditionalJump(middle, Condition.LE, false);
417                registerEffort(left, middle, ++depth);
418
419                if (middle + 1 == right) {
420                    closure.conditionalJumpOrDefault(right, Condition.EQ, rightBorder);
421                    registerEffort(right, right, ++depth);
422                    registerDefaultEffort(depth);
423                } else {
424                    if (keyConstants[middle].asLong() + 1 != keyConstants[middle + 1].asLong()) {
425                        closure.conditionalJump(middle + 1, Condition.LT, true);
426                        registerDefaultEffort(++depth);
427                    }
428                    if (getSliceEnd(closure, middle + 1) == right) {
429                        if (right == keyConstants.length - 1 || keyConstants[right].asLong() + 1 != keyConstants[right + 1].asLong()) {
430                            closure.conditionalJumpOrDefault(right, Condition.LE, rightBorder);
431                            registerEffort(middle + 1, right, ++depth);
432                            registerDefaultEffort(depth);
433                        } else {
434                            closure.conditionalJump(middle + 1, null, false);
435                            registerEffort(middle + 1, right, depth);
436                        }
437                    } else {
438                        recurseBinarySwitch(closure, middle + 1, right, depth);
439                    }
440                }
441            } else if (getSliceEnd(closure, middle + 1) == right) {
442                if (rightBorder || keyConstants[right].asLong() + 1 != keyConstants[right + 1].asLong()) {
443                    closure.conditionalJump(right, Condition.GT, true);
444                    registerDefaultEffort(++depth);
445                }
446                closure.conditionalJump(middle + 1, Condition.GE, false);
447                registerEffort(middle + 1, right, ++depth);
448                recurseBinarySwitch(closure, left, middle, depth);
449            } else {
450                Label label = closure.conditionalJump(middle + 1, Condition.GE);
451                depth++;
452                recurseBinarySwitch(closure, left, middle, depth);
453                closure.bind(label);
454                recurseBinarySwitch(closure, middle + 1, right, depth);
455            }
456        }
457    }
458
459    public abstract void run(SwitchClosure closure);
460
461    private static SwitchStrategy[] getStrategies(double[] keyProbabilities, JavaConstant[] keyConstants, LabelRef[] keyTargets) {
462        SwitchStrategy[] strategies = new SwitchStrategy[]{new SequentialStrategy(keyProbabilities, keyConstants), new RangesStrategy(keyProbabilities, keyConstants),
463                        new BinaryStrategy(keyProbabilities, keyConstants)};
464        for (SwitchStrategy strategy : strategies) {
465            strategy.effortClosure = strategy.new EffortClosure(keyTargets);
466            strategy.run(strategy.effortClosure);
467            strategy.averageEffort = strategy.effortClosure.getAverageEffort();
468            strategy.effortClosure = null;
469        }
470        return strategies;
471    }
472
473    /**
474     * Creates all switch strategies for the given switch, evaluates them (based on average effort)
475     * and returns the best one.
476     */
477    public static SwitchStrategy getBestStrategy(double[] keyProbabilities, JavaConstant[] keyConstants, LabelRef[] keyTargets) {
478        SwitchStrategy[] strategies = getStrategies(keyProbabilities, keyConstants, keyTargets);
479        double bestEffort = Integer.MAX_VALUE;
480        SwitchStrategy bestStrategy = null;
481        for (SwitchStrategy strategy : strategies) {
482            if (strategy.getAverageEffort() < bestEffort) {
483                bestEffort = strategy.getAverageEffort();
484                bestStrategy = strategy;
485            }
486        }
487        return bestStrategy;
488    }
489}