001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.adaptive.database.callgraph;
014    
015    import org.jikesrvm.classloader.RVMMethod;
016    import org.jikesrvm.runtime.RuntimeEntrypoints;
017    
018    /**
019     * A collection of weighted call targets.
020     * Depending on the size of the callee set, we use different subclasses
021     * that optimize the time/space tradeoffs.
022     */
023    public abstract class WeightedCallTargets {
024    
025      /**
026       * Iterate over all of the targets, evaluating the argument function on each edge.<p>
027       * NOTE: We guarantee that the targets will be iterated in monotonically decreasing
028       *       edge weight. This simplifies the coding of the inlining clients that consume
029       *       this information.
030       * @param func the function to evaluate on each target
031       */
032      public abstract void visitTargets(Visitor func);
033    
034      /**
035       * Augment the weight associated with the argument method by 1.<p>
036       * NOTE: This method may change the representation of the target
037       * method.  The caller must be sure to update their backing store of
038       * WeightedCallTargets accordingly to avoid losing the update.
039       */
040      public final WeightedCallTargets incrementCount(RVMMethod target) {
041        return augmentCount(target, 1);
042      }
043    
044      /**
045       * Augment the weight associated with the argument method by the argument amount.
046       * NOTE: This method may change the representation of the target
047       * method.  The caller must be sure to update their backing store of
048       * WeightedCallTargets accordingly to avoid losing the update.
049       */
050      public abstract WeightedCallTargets augmentCount(RVMMethod target, double amount);
051    
052      /**
053       * Decay the weights of all call targets by the specified amount
054       * @param rate the value to decay by
055       */
056      public abstract void decay(double rate);
057    
058      /**
059       * totalWeight of all call targets
060       */
061      public abstract double totalWeight();
062    
063      /**
064       * @param goal RVMMethod that is the statically possible target
065       * @param isPrecise whether or not goal is a precise target, or should be
066       *        interpreted as being the root of a virtual method family, any of which
067       *        are statically possible.
068       * @return the filtered call targets or {@code null} if no such target exists
069       */
070      public abstract WeightedCallTargets filter(RVMMethod goal, boolean isPrecise);
071    
072      public static WeightedCallTargets create(RVMMethod target, double weight) {
073        return new SingleTarget(target, weight);
074      }
075    
076      /**
077       * Used by visitTargets
078       */
079      public interface Visitor {
080        void visit(RVMMethod target, double weight);
081      }
082    
083      /**
084       * An implementation for storing a call site distribution that has a single target.
085       */
086      private static final class SingleTarget extends WeightedCallTargets {
087        private final RVMMethod target;
088        private float weight;
089    
090        SingleTarget(RVMMethod t, double w) {
091          target = t;
092          weight = (float) w;
093        }
094    
095        @Override
096        public void visitTargets(Visitor func) {
097          func.visit(target, weight);
098        }
099    
100        @Override
101        public WeightedCallTargets augmentCount(RVMMethod t, double v) {
102          if (target.equals(t)) {
103            weight += v;
104            return this;
105          } else {
106            MultiTarget ms = new MultiTarget();
107            ms.augmentCount(target, weight);
108            ms.augmentCount(t, v);
109            return ms;
110          }
111        }
112    
113        @Override
114        public void decay(double rate) {
115          weight /= rate;
116        }
117    
118        @Override
119        public double totalWeight() { return weight; }
120    
121        @Override
122        public WeightedCallTargets filter(RVMMethod goal, boolean isPrecise) {
123          if (isPrecise) {
124            return (goal.equals(target)) ? this : null;
125          } else {
126            if (goal.equals(target)) {
127              return this;
128            }
129            if (RuntimeEntrypoints.isAssignableWith(goal.getDeclaringClass(), target.getDeclaringClass())) {
130              return this;
131            } else {
132              return null;
133            }
134          }
135        }
136      }
137    
138      /**
139       * An implementation for storing a call site distribution that has multiple targets.
140       */
141      private static final class MultiTarget extends WeightedCallTargets {
142        RVMMethod[] methods = new RVMMethod[5];
143        float[] weights = new float[5];
144    
145        @Override
146        public synchronized void visitTargets(Visitor func) {
147          // Typically expect elements to be "almost" sorted due to previous sorting operations.
148          // When this is true, expected time for insertion sort is O(n).
149          for (int i = 1; i < methods.length; i++) {
150            RVMMethod m = methods[i];
151            if (m != null) {
152              float w = weights[i];
153              int j = i;
154              while (j > 0 && weights[j - 1] < w) {
155                methods[j] = methods[j - 1];
156                weights[j] = weights[j - 1];
157                j--;
158              }
159              methods[j] = m;
160              weights[j] = w;
161            }
162          }
163    
164          for (int i = 0; i < methods.length; i++) {
165            if (methods[i] != null) {
166              func.visit(methods[i], weights[i]);
167            }
168          }
169        }
170    
171        @Override
172        public synchronized WeightedCallTargets augmentCount(RVMMethod t, double v) {
173          int empty = -1;
174          for (int i = 0; i < methods.length; i++) {
175            if (methods[i] != null) {
176              if (methods[i].equals(t)) {
177                weights[i] += v;
178                return this;
179              }
180            } else {
181              if (empty == -1) { empty = i; }
182            }
183          }
184    
185          // New target must be added
186          if (empty == -1) {
187            // must grow arrays
188            empty = methods.length;
189            RVMMethod[] newM = new RVMMethod[methods.length * 2];
190            System.arraycopy(methods, 0, newM, 0, methods.length);
191            methods = newM;
192            float[] newW = new float[weights.length * 2];
193            System.arraycopy(weights, 0, newW, 0, weights.length);
194            weights = newW;
195          }
196    
197          methods[empty] = t;
198          weights[empty] = (float) v;
199          return this;
200        }
201    
202        @Override
203        public synchronized void decay(double rate) {
204          for (int i = 0; i < weights.length; i++) {
205            weights[i] /= rate;
206          }
207        }
208    
209        @Override
210        public synchronized double totalWeight() {
211          double sum = 0;
212          for (float weight : weights) {
213            sum += weight;
214          }
215          return sum;
216        }
217    
218        @Override
219        public synchronized WeightedCallTargets filter(RVMMethod goal, boolean isPrecise) {
220          if (isPrecise) {
221            for (int i = 0; i < methods.length; i++) {
222              if (goal.equals(methods[i])) {
223                return WeightedCallTargets.create(methods[i], weights[i]);
224              }
225            }
226            return null;
227          } else {
228            int matchCount = 0;
229            int mismatchCount = 0;
230            for (RVMMethod method : methods) {
231              if (method != null) {
232                if (RuntimeEntrypoints.isAssignableWith(goal.getDeclaringClass(), method.getDeclaringClass())) {
233                  matchCount++;
234                } else {
235                  mismatchCount++;
236                }
237              }
238            }
239            if (mismatchCount == 0) {
240              return this;
241            }
242            if (matchCount == 0) {
243              return null;
244            }
245    
246            MultiTarget filtered = new MultiTarget();
247            for (int i=0; i<methods.length; i++) {
248              RVMMethod method = methods[i];
249              if (method != null && RuntimeEntrypoints.isAssignableWith(goal.getDeclaringClass(), method.getDeclaringClass())) {
250                filtered.augmentCount(method, weights[i]);
251              }
252            }
253            return filtered;
254          }
255        }
256      }
257    }