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.objectmodel;
014    
015    import org.jikesrvm.VM;
016    import org.jikesrvm.SizeConstants;
017    import org.jikesrvm.classloader.RVMClass;
018    import org.jikesrvm.classloader.RVMField;
019    import org.vmmagic.unboxed.Offset;
020    
021    /**
022     * This abstract class defines the interface for schemes that layout fields
023     * in an object.  Not header fields, (scalar) object fields.
024     * <p>
025     * The field layout object encapsulates layout state.
026     */
027    public abstract class FieldLayout implements SizeConstants {
028    
029      /**
030       * Enable debugging
031       */
032      protected static final boolean DEBUG = false;
033    
034      /** Whether to lay out 8byte values first in order to avoid some holes */
035      private final boolean largeFieldsFirst;
036    
037      /** Lay out reference fields in a block */
038      private final boolean clusterReferenceFields;
039    
040      public FieldLayout(boolean largeFieldsFirst, boolean clusterReferenceFields) {
041        this.largeFieldsFirst = largeFieldsFirst;
042        this.clusterReferenceFields = clusterReferenceFields;
043      }
044    
045      /**
046       * Maximum of two integers
047       */
048      protected static int max(int x, int y) {
049        return (x > y) ? x : y;
050      }
051    
052      /**
053       * Log base 2 of an integer
054       */
055      protected static int log2(int x) {
056        int logSize = 0;
057        while ((1 << logSize) < x) {
058          logSize += 1;
059        }
060        return logSize;
061      }
062    
063      /*
064      * Abstract methods that determine the behaviour of a particular layout scheme
065      */
066    
067      /**
068       * Return the appropriate layout context object for the given class.
069       *
070       * @param klass The class
071       * @return The layout context
072       */
073      protected abstract FieldLayoutContext getLayoutContext(RVMClass klass);
074    
075      /**
076       * This is where a class gets laid out.  Differences in layout strategy
077       * are largely encapsulated in the layoutContext object.
078       *
079       * @param klass The class to lay out.
080       */
081      public void layoutInstanceFields(RVMClass klass) {
082        /*
083         * Determine available field slots from parent classes, and allocate
084         * a new context object for this class and its children.
085         */
086        FieldLayoutContext fieldLayout = getLayoutContext(klass);
087    
088        // Preferred alignment of object - modified to reflect added fields
089        // New fields to be allocated for this object
090        RVMField[] fields = klass.getDeclaredFields();
091    
092        if (DEBUG) {
093          VM.sysWriteln("Laying out: ", klass.toString());
094        }
095    
096        /*
097        * Layout reference fields first pre-pass - This can help some
098        * GC schemes.
099        */
100        if (clusterReferenceFields) {
101          // For every field
102          for (RVMField field : fields) {
103            if (!field.isStatic() && !field.hasOffset()) {
104              if (field.isReferenceType()) {
105                layoutField(fieldLayout, klass, field, BYTES_IN_ADDRESS);
106              }
107            }
108          }
109        }
110    
111        /*
112        * Layout 8byte values first pre-pass - do this to avoid unnecessary
113        * holes for object layouts such as an int followed by a long
114        */
115        if (largeFieldsFirst) {
116          // For every field
117          for (RVMField field : fields) {
118            // Should we allocate space in the object now?
119            if (!field.isStatic() && !field.hasOffset()) {
120              if (field.getSize() == BYTES_IN_LONG) {
121                layoutField(fieldLayout, klass, field, BYTES_IN_LONG);
122              }
123            }
124          }
125        }
126    
127        for (RVMField field : fields) {                               // For every field
128          int fieldSize = field.getSize();                            // size of field
129          if (!field.isStatic() && !field.hasOffset()) {              // Allocate space in the object?
130            layoutField(fieldLayout, klass, field, fieldSize);
131          }
132        }
133        // JavaHeader requires objects to be int sized/aligned
134        if (VM.VerifyAssertions) VM._assert((fieldLayout.getObjectSize() & 0x3) == 0);
135    
136        /* Update class to reflect changes */
137    
138        updateClass(klass, fieldLayout);
139      }
140    
141      /**
142       * Update the RVMClass with context info.
143       *
144       * @param klass
145       * @param fieldLayout
146       */
147      protected void updateClass(RVMClass klass, FieldLayoutContext fieldLayout) {
148        /*
149         * Save the new field layout.
150         */
151        klass.setFieldLayoutContext(fieldLayout);
152    
153        klass.setInstanceSizeInternal(ObjectModel.computeScalarHeaderSize(klass) + fieldLayout.getObjectSize());
154        klass.setAlignment(fieldLayout.getAlignment());
155      }
156    
157      /**
158       * Update a field to set its offset within the object.
159       *
160       * @param klass
161       * @param field
162       * @param offset
163       */
164      protected void setOffset(RVMClass klass, RVMField field, int offset) {
165    
166        Offset fieldOffset;
167        if (offset >= 0) {
168          fieldOffset =
169              Offset.fromIntSignExtend(JavaHeader.objectStartOffset(klass) +
170                                       ObjectModel.computeScalarHeaderSize(klass) +
171                                       offset);
172        } else {
173          /* Negative offsets go before the header */
174          fieldOffset = Offset.fromIntSignExtend(JavaHeader.objectStartOffset(klass) + offset);
175        }
176        field.setOffset(fieldOffset);
177        if (DEBUG) {
178          VM.sysWrite("  field: ", field.toString());
179          VM.sysWriteln(" offset ", fieldOffset.toInt());
180        }
181      }
182    
183      /**
184       * Lay out a given field.
185       *
186       * @param layout State for the layout process
187       * @param klass The class whose fields we're laying out.
188       * @param field The field we are laying out.
189       * @param fieldSize The size of the field.
190       */
191      protected void layoutField(FieldLayoutContext layout, RVMClass klass, RVMField field, int fieldSize) {
192        boolean isRef = field.isReferenceType();
193        setOffset(klass, field, layout.nextOffset(fieldSize, isRef));
194      }
195    }