/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.objectmodel;
import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_LONG;
import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMField;
import org.vmmagic.unboxed.Offset;
/**
* This abstract class defines the interface for schemes that layout fields
* in an object. Not header fields, (scalar) object fields.
* <p>
* The field layout object encapsulates layout state.
*/
public abstract class FieldLayout {
/**
* Enable debugging
*/
protected static final boolean DEBUG = false;
/** Whether to lay out 8byte values first in order to avoid some holes */
private final boolean largeFieldsFirst;
/** Lay out reference fields in a block */
private final boolean clusterReferenceFields;
public FieldLayout(boolean largeFieldsFirst, boolean clusterReferenceFields) {
this.largeFieldsFirst = largeFieldsFirst;
this.clusterReferenceFields = clusterReferenceFields;
}
/**
* @param x the integer
* @return log base 2 of an integer
*/
protected static int log2(int x) {
int logSize = 0;
while ((1 << logSize) < x) {
logSize += 1;
}
return logSize;
}
/*
* Abstract methods that determine the behaviour of a particular layout scheme
*/
/**
* Return the appropriate layout context object for the given class.
*
* @param klass The class
* @return The layout context
*/
protected abstract FieldLayoutContext getLayoutContext(RVMClass klass);
/**
* This is where a class gets laid out. Differences in layout strategy
* are largely encapsulated in the layoutContext object.
*
* @param klass The class to lay out.
*/
public void layoutInstanceFields(RVMClass klass) {
/*
* Determine available field slots from parent classes, and allocate
* a new context object for this class and its children.
*/
FieldLayoutContext fieldLayout = getLayoutContext(klass);
// Preferred alignment of object - modified to reflect added fields
// New fields to be allocated for this object
RVMField[] fields = klass.getDeclaredFields();
if (DEBUG) {
VM.sysWriteln("Laying out: ", klass.toString());
}
/*
* Layout reference fields first pre-pass - This can help some
* GC schemes.
*/
if (clusterReferenceFields) {
// For every field
for (RVMField field : fields) {
if (!field.isStatic() && !field.hasOffset()) {
if (field.isReferenceType()) {
layoutField(fieldLayout, klass, field, BYTES_IN_ADDRESS);
}
}
}
}
/*
* Layout 8byte values first pre-pass - do this to avoid unnecessary
* holes for object layouts such as an int followed by a long
*/
if (largeFieldsFirst) {
// For every field
for (RVMField field : fields) {
// Should we allocate space in the object now?
if (!field.isStatic() && !field.hasOffset()) {
if (field.getSize() == BYTES_IN_LONG) {
layoutField(fieldLayout, klass, field, BYTES_IN_LONG);
}
}
}
}
for (RVMField field : fields) { // For every field
int fieldSize = field.getSize(); // size of field
if (!field.isStatic() && !field.hasOffset()) { // Allocate space in the object?
layoutField(fieldLayout, klass, field, fieldSize);
}
}
// JavaHeader requires objects to be int sized/aligned
if (VM.VerifyAssertions) VM._assert((fieldLayout.getObjectSize() & 0x3) == 0);
/* Update class to reflect changes */
updateClass(klass, fieldLayout);
}
/**
* Updates the RVMClass with context info.
*
* @param klass the class to update
* @param fieldLayout the layout for the class
*/
protected void updateClass(RVMClass klass, FieldLayoutContext fieldLayout) {
/*
* Save the new field layout.
*/
klass.setFieldLayoutContext(fieldLayout);
klass.setInstanceSizeInternal(ObjectModel.computeScalarHeaderSize(klass) + fieldLayout.getObjectSize());
klass.setAlignment(fieldLayout.getAlignment());
}
/**
* Update a field to set its offset within the object.
*
* @param klass the class that the field belongs to
* @param field the field
* @param offset the new offset for the field
*/
protected void setOffset(RVMClass klass, RVMField field, int offset) {
Offset fieldOffset;
if (offset >= 0) {
fieldOffset =
Offset.fromIntSignExtend(JavaHeader.objectStartOffset(klass) +
ObjectModel.computeScalarHeaderSize(klass) +
offset);
} else {
/* Negative offsets go before the header */
fieldOffset = Offset.fromIntSignExtend(JavaHeader.objectStartOffset(klass) + offset);
}
field.setOffset(fieldOffset);
if (DEBUG) {
VM.sysWrite(" field: ", field.toString());
VM.sysWriteln(" offset ", fieldOffset.toInt());
}
}
/**
* Lay out a given field.
*
* @param layout State for the layout process
* @param klass The class whose fields we're laying out.
* @param field The field we are laying out.
* @param fieldSize The size of the field in bytes
*/
protected void layoutField(FieldLayoutContext layout, RVMClass klass, RVMField field, int fieldSize) {
boolean isRef = field.isReferenceType();
setOffset(klass, field, layout.nextOffset(fieldSize, isRef));
}
}