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