1 cananian 1.1.2.1 // PackedClassFieldMap.java, created Wed Jul 11 20:26:56 2001 by cananian
  2 cananian 1.1.2.1 // Copyright (C) 2000 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1.2.1 package harpoon.Backend.RuntimeTiny;
  5 cananian 1.1.2.1 
  6 cananian 1.1.2.1 import harpoon.Backend.Maps.FieldMap;
  7 cananian 1.1.2.1 import harpoon.ClassFile.HClass;
  8 cananian 1.1.2.1 import harpoon.ClassFile.HField;
  9 cananian 1.1.2.1 import harpoon.Util.Util;
 10 cananian 1.1.2.1 
 11 cananian 1.1.2.1 import java.util.ArrayList;
 12 cananian 1.1.2.1 import java.util.Arrays;
 13 cananian 1.1.2.1 import java.util.Collections;
 14 cananian 1.1.2.1 import java.util.Comparator;
 15 cananian 1.1.2.1 import java.util.HashMap;
 16 cananian 1.1.2.1 import java.util.Iterator;
 17 cananian 1.1.2.1 import java.util.LinkedList;
 18 cananian 1.1.2.1 import java.util.List;
 19 cananian 1.1.2.1 import java.util.ListIterator;
 20 cananian 1.1.2.1 import java.util.Map;
 21 cananian 1.1.2.1 /**
 22 cananian 1.1.2.1  * <code>PackedClassFieldMap</code> is a <code>FieldMap</code> for
 23 cananian 1.1.2.1  * non-static fields of a class which attempts to maximally fill holes
 24 cananian 1.1.2.1  * in the data structure (even if this means commingling a subclass'
 25 cananian 1.1.2.1  * fields with those of its superclass) in order to minimize the
 26 cananian 1.1.2.1  * space required by objects.
 27 cananian 1.1.2.1  * 
 28 cananian 1.1.2.1  * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 29 cananian 1.2      * @version $Id: TinyPackedClassFieldMap.java,v 1.2 2002/04/10 03:03:43 cananian Exp $
 30 cananian 1.1.2.1  */
 31 cananian 1.1.2.1 public abstract class TinyPackedClassFieldMap extends FieldMap {
 32 cananian 1.1.2.1     final Runtime runtime;
 33 cananian 1.1.2.1     /** Creates a <code>PackedClassFieldMap</code>. */
 34 cananian 1.1.2.1     public TinyPackedClassFieldMap(Runtime runtime) {
 35 cananian 1.1.2.1         this.runtime = runtime;
 36 cananian 1.1.2.1     }
 37 cananian 1.1.2.1     
 38 cananian 1.1.2.1     /** Override this function to indicate a *preferred* but not *mandatory*
 39 cananian 1.1.2.1      *  alignment for the given field.  We will "try hard" to honor this
 40 cananian 1.1.2.1      *  alignment, but not if it would mean increasing the object size. */
 41 cananian 1.1.2.1     public int fieldPreferredAlignment(HField hf){ return fieldAlignment(hf); }
 42 cananian 1.1.2.1 
 43 cananian 1.1.2.1     /** Return an offset to the given field. */
 44 cananian 1.1.2.1     public int fieldOffset(HField hf) {
 45 cananian 1.1.2.1         assert !hf.isStatic();
 46 cananian 1.1.2.1         if (!fieldcache.containsKey(hf)) do_one(hf.getDeclaringClass());
 47 cananian 1.1.2.1         assert fieldcache.containsKey(hf);
 48 cananian 1.1.2.1         return fieldcache.get(hf).intValue();
 49 cananian 1.1.2.1     }
 50 cananian 1.1.2.1     /** Return an unmodifiable List over all appropriate fields in the given
 51 cananian 1.1.2.1      *  class, in order from smallest to largest offset. */
 52 cananian 1.1.2.1     public List<HField> fieldList(HClass hc) {
 53 cananian 1.1.2.1         // get list of all fields in this class and its parents.
 54 cananian 1.1.2.1         List<HField> l = new ArrayList<HField>();
 55 cananian 1.1.2.1         for (HClass p = hc; p!=null; p=p.getSuperclass())
 56 cananian 1.1.2.1             l.addAll(Arrays.asList(p.getDeclaredFields()));
 57 cananian 1.1.2.1         // weed out static fields
 58 cananian 1.1.2.1         for (Iterator<HField> it=l.iterator(); it.hasNext(); )
 59 cananian 1.1.2.1             if (it.next().isStatic())
 60 cananian 1.1.2.1                 it.remove();
 61 cananian 1.1.2.1         // now sort by fieldOffset.
 62 cananian 1.1.2.1         Collections.sort(l, new Comparator<HField>() {
 63 cananian 1.1.2.1             public int compare(HField hf1, HField hf2) {
 64 cananian 1.1.2.1                 return fieldOffset(hf1) - fieldOffset(hf2);
 65 cananian 1.1.2.1             }
 66 cananian 1.1.2.1         });
 67 cananian 1.1.2.1         // quick verification of well-formedness
 68 cananian 1.1.2.1         // (offsets must be strictly increasing)
 69 cananian 1.1.2.1         int last=-100;
 70 cananian 1.1.2.1         for (Iterator<HField> it=l.iterator(); it.hasNext(); ) {
 71 cananian 1.1.2.1             int off=fieldOffset(it.next());
 72 cananian 1.1.2.1             assert last<off : "Ill-formed field list";
 73 cananian 1.1.2.1             last = off;
 74 cananian 1.1.2.1         }
 75 cananian 1.1.2.1         // done!
 76 cananian 1.1.2.1         return l;
 77 cananian 1.1.2.1     }
 78 cananian 1.1.2.1 
 79 cananian 1.1.2.1     /** this method assigns "good" offsets to all the fields in class hc,
 80 cananian 1.1.2.1      *  using the assignments in hc's superclass and the list of free space
 81 cananian 1.1.2.1      *  in the superclass as starting points. */
 82 cananian 1.1.2.1     private void do_one(HClass hc) {
 83 cananian 1.1.2.1         assert hc!=null;
 84 cananian 1.1.2.1         // get the 'free space' structure of the parent.
 85 cananian 1.1.2.1         List<Interval> free = new LinkedList<Interval>
 86 cananian 1.1.2.1             (Arrays.asList(freespace(hc.getSuperclass())));
 87 cananian 1.1.2.1         // get all the fields of this class that we have to allocate.
 88 cananian 1.1.2.1         List<HField> l =
 89 cananian 1.1.2.1             new ArrayList<HField>(Arrays.asList(hc.getDeclaredFields()));
 90 cananian 1.1.2.1         // weed out static fields
 91 cananian 1.1.2.1         for (Iterator<HField> it=l.iterator(); it.hasNext(); )
 92 cananian 1.1.2.1             if (it.next().isStatic())
 93 cananian 1.1.2.1                 it.remove();
 94 cananian 1.1.2.1         // we're going to allocate fields from largest to smallest,
 95 cananian 1.1.2.1         // so sort them first. (primarily sort by size, then by alignment)
 96 cananian 1.1.2.1         Collections.sort(l, new Comparator<HField>() {
 97 cananian 1.1.2.1             public int compare(HField hf1, HField hf2) {
 98 cananian 1.1.2.1                 // note reversed comparison here so that largest end up first.
 99 cananian 1.1.2.1                 int r = fieldSize(hf2) - fieldSize(hf1);
100 cananian 1.1.2.1                 if (r==0) r = fieldPreferredAlignment(hf2)
101 cananian 1.1.2.1                               - fieldPreferredAlignment(hf1);
102 cananian 1.1.2.1                 if (r==0) r = fieldAlignment(hf2) - fieldAlignment(hf1);
103 cananian 1.1.2.1                 return r;
104 cananian 1.1.2.1             }
105 cananian 1.1.2.1         });
106 cananian 1.1.2.1         // now allocate them one by one (largest to smallest, like we said)
107 cananian 1.1.2.1         // the alloc_one method has the side effect of adding an entry to
108 cananian 1.1.2.1         // the fieldcache.
109 cananian 1.1.2.1         nextfield:
110 cananian 1.1.2.1         while (!l.isEmpty()) {
111 cananian 1.1.2.1             // allocate largest *must-align* field
112 cananian 1.1.2.1             for (Iterator<HField> it=l.iterator(); it.hasNext(); )
113 cananian 1.1.2.1                 if (alloc_one(it.next(), free, true, false)) {
114 cananian 1.1.2.1                     it.remove();
115 cananian 1.1.2.1                     continue nextfield;
116 cananian 1.1.2.1                 }
117 cananian 1.1.2.1             // allocate largest *preferably-aligned* field
118 cananian 1.1.2.1             for (Iterator<HField> it=l.iterator(); it.hasNext(); )
119 cananian 1.1.2.1                 if (alloc_one(it.next(), free, false, true)) {
120 cananian 1.1.2.1                     it.remove();
121 cananian 1.1.2.1                     continue nextfield;
122 cananian 1.1.2.1                 }
123 cananian 1.1.2.1             // allocate smalled unaligned field.
124 cananian 1.1.2.1             if (alloc_one(l.remove(l.size()-1), free, false, false))
125 cananian 1.1.2.1                 continue nextfield;
126 cananian 1.1.2.1             // should never get here!
127 cananian 1.1.2.1             assert false : "should always be able to alloc smallest field";
128 cananian 1.1.2.1         }
129 cananian 1.1.2.1         // cache away the free list for the next guy (subclasses of this)
130 cananian 1.1.2.1         freecache.put(hc, free.toArray(new Interval[free.size()]));
131 cananian 1.1.2.1         // done!
132 cananian 1.1.2.1     }
133 cananian 1.1.2.1     /** this method tries to allocate the field hf as low in the object
134 cananian 1.1.2.1      *  as possible, using the current list of free spaces in the class. */
135 cananian 1.1.2.1     private boolean alloc_one(HField hf, List<Interval> free,
136 cananian 1.1.2.1                               boolean mustAlign, boolean prefAlign) {
137 cananian 1.1.2.1         int size = fieldSize(hf), align = fieldAlignment(hf);
138 cananian 1.1.2.1         int palign = fieldPreferredAlignment(hf);
139 cananian 1.1.2.1         // 'mustAlign' fields have align > 1
140 cananian 1.1.2.1         if (align<=1 && mustAlign) return false;
141 cananian 1.1.2.1         // go through free list, looking for a place to put this.
142 cananian 1.1.2.1         // first, try to put it at its preferred alignment, but not at the end
143 cananian 1.1.2.1         // as a last resort, put it anywhere, ignoring the preferred alignment.
144 cananian 1.1.2.1         for (int pass=0; pass<2; pass++) {
145 cananian 1.1.2.1           for (ListIterator<Interval> li = free.listIterator(); li.hasNext();){
146 cananian 1.1.2.1             Interval i = li.next();
147 cananian 1.1.2.1             assert i.low < i.high; // validity of interval.
148 cananian 1.1.2.1             // use preferred alignment for first pass, but don't allow alloc
149 cananian 1.1.2.1             // at end (in 'infinite interval')
150 cananian 1.1.2.1             int malign = (pass==0) ? palign : align;
151 cananian 1.1.2.1             if (pass==0 && !li.hasNext()) continue;
152 cananian 1.1.2.1             // l and h will be the boundaries of the field, if we put it
153 cananian 1.1.2.1             // in this interval.
154 cananian 1.1.2.1             int l = i.low;
155 cananian 1.1.2.1             // if 'mustAlign' boot all fields which aren't perfectly aligned.
156 cananian 1.1.2.1             if (mustAlign && (!li.hasNext()) && 0!=(l%align)) continue;
157 cananian 1.1.2.1             // if 'prefAlign' boot fields which aren't preferentially aligned
158 cananian 1.1.2.1             if (prefAlign && (!li.hasNext()) && 0!=(l%palign)) continue;
159 cananian 1.1.2.1             // okay, otherwise add padding if necessary.
160 cananian 1.1.2.2             while ((l%malign)!=0) l++; // (slow, but works even w/ negative l)
161 cananian 1.1.2.1             int h = l + size;
162 cananian 1.1.2.1             if (h <= i.high) { // yay, the field fits here!
163 cananian 1.1.2.1                 // update interval list.
164 cananian 1.1.2.1                 Interval bot = new Interval(i.low, l);
165 cananian 1.1.2.1                 Interval top = new Interval(h, i.high);
166 cananian 1.1.2.1                 li.remove(); // replace the old interval with the split ones
167 cananian 1.1.2.1                 if (bot.low < bot.high) li.add(bot);
168 cananian 1.1.2.1                 if (top.low < top.high) li.add(top);
169 cananian 1.1.2.1                 // cache field offset.
170 cananian 1.1.2.1                 fieldcache.put(hf, new Integer(l));
171 cananian 1.1.2.1                 // done!
172 cananian 1.1.2.1                 return true; // successfully alloc'ed this field
173 cananian 1.1.2.1             }
174 cananian 1.1.2.1           }
175 cananian 1.1.2.1         }
176 cananian 1.1.2.1         return false; // unable to alloc this field, given our constraints
177 cananian 1.1.2.1     }
178 cananian 1.1.2.1     /** maps fields to already-computed offsets */
179 cananian 1.1.2.1     private final Map<HField,Integer> fieldcache =
180 cananian 1.1.2.1         new HashMap<HField,Integer>();
181 cananian 1.1.2.1 
182 cananian 1.1.2.1     /** this method returns the cached 'free space' list for class hc.
183 cananian 1.1.2.1      *  if the free space list for hc has not already been created and
184 cananian 1.1.2.1      *  cached, invokes do_one(hc) to create and cache it.  hc==null
185 cananian 1.1.2.1      *  indicates the "non-existent superclass of everything", and
186 cananian 1.1.2.1      *  naturally fields can be allocated anywhere in it.  This is
187 cananian 1.1.2.1      *  represented as the interval [0, infinity). */
188 cananian 1.1.2.1     private Interval[] freespace(HClass hc) {
189 cananian 1.1.2.1         if (hc==null)
190 cananian 1.1.2.1             if (runtime.clazBytes==4)
191 cananian 1.1.2.1                 return new Interval[] { new Interval(0, Integer.MAX_VALUE) };
192 cananian 1.1.2.3             else if (runtime.hashlockShrink)
193 cananian 1.1.2.3                 return new Interval[] { new Interval(-4+runtime.clazBytes,
194 cananian 1.1.2.3                                                      Integer.MAX_VALUE) };
195 cananian 1.1.2.1             else
196 cananian 1.1.2.1                 return new Interval[] { new Interval(-8+runtime.clazBytes, -4),
197 cananian 1.1.2.1                                         new Interval(0, Integer.MAX_VALUE) };
198 cananian 1.1.2.1         if (!freecache.containsKey(hc)) do_one(hc);
199 cananian 1.1.2.1         assert freecache.containsKey(hc);
200 cananian 1.1.2.1         return freecache.get(hc);
201 cananian 1.1.2.1     }
202 cananian 1.1.2.1     /** maps classes to 'free space' lists. */
203 cananian 1.1.2.1     private final Map<HClass,Interval[]> freecache =
204 cananian 1.1.2.1         new HashMap<HClass,Interval[]>();
205 cananian 1.1.2.1 
206 cananian 1.1.2.1     /** This class represents an open interval in the class, which we
207 cananian 1.1.2.1      *  may assign a field to (if it fits). */
208 cananian 1.1.2.1     private static class Interval {
209 cananian 1.1.2.1         final int low, high;
210 cananian 1.1.2.1         Interval(int low, int high) { this.low = low; this.high=high; }
211 cananian 1.1.2.1     }
212 cananian 1.2     }