/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.hub;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.core.common.calc.UnsignedMath;
import org.graalvm.compiler.nodes.java.ArrayLengthNode;
import org.graalvm.compiler.word.Word;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.annotate.DuplicatedInNativeCode;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.config.ObjectLayout;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.util.VMError;
import jdk.vm.ci.meta.ResolvedJavaType;
The layout encoding for instances is the aligned instance size (i.e., a positive number).
For arrays, the layout encoding is a negative number with the following format:
[tag:2, free:10, base:12, indexShift:8]
- tag: 0x80 if the array is an object array, 0xC0 if it is a primitive array
- free: currently unused bits
- base: the array base offset
- indexShift: the array index shift for accessing array elements or for computing the array
size based on the array length
/**
* The layout encoding for instances is the aligned instance size (i.e., a positive number).
* <p>
* For arrays, the layout encoding is a negative number with the following format:<br>
*
* <code>[tag:2, free:10, base:12, indexShift:8]</code>
* <ul>
* <li>tag: 0x80 if the array is an object array, 0xC0 if it is a primitive array</li>
* <li>free: currently unused bits</li>
* <li>base: the array base offset</li>
* <li>indexShift: the array index shift for accessing array elements or for computing the array
* size based on the array length</li>
* </ul>
*/
@DuplicatedInNativeCode
public class LayoutEncoding {
private static final int NEUTRAL_VALUE = 0;
private static final int PRIMITIVE_VALUE = NEUTRAL_VALUE + 1;
private static final int INTERFACE_VALUE = PRIMITIVE_VALUE + 1;
private static final int ABSTRACT_VALUE = INTERFACE_VALUE + 1;
private static final int LAST_SPECIAL_VALUE = ABSTRACT_VALUE;
private static final int ARRAY_INDEX_SHIFT_SHIFT = 0;
private static final int ARRAY_INDEX_SHIFT_MASK = 0xff;
private static final int ARRAY_BASE_SHIFT = 8 + ARRAY_INDEX_SHIFT_SHIFT;
private static final int ARRAY_BASE_MASK = 0xfff;
private static final int ARRAY_TAG_BITS = 2;
private static final int ARRAY_TAG_SHIFT = Integer.SIZE - ARRAY_TAG_BITS;
private static final int ARRAY_TAG_PRIMITIVE_VALUE = ~0x00;
private static final int ARRAY_TAG_OBJECT_VALUE = ~0x01;
public static int forPrimitive() {
return PRIMITIVE_VALUE;
}
public static int forInterface() {
return INTERFACE_VALUE;
}
public static int forAbstract() {
return ABSTRACT_VALUE;
}
@Platforms(Platform.HOSTED_ONLY.class)
private static void guaranteeEncoding(ResolvedJavaType type, boolean condition, String description) {
VMError.guarantee(condition, description + ". This error is caused by an incorrect compact encoding of a type " +
"(a class, array or a primitive). The error occurred with the following type, but also could be caused " +
"by characteristics of the overall type hierarchy: " + type + ". Please report this problem and the " +
"conditions in which it occurs and include any noteworthy characteristics of the type hierarchy and " +
"architecture of the application and the libraries and frameworks it uses.");
}
@Platforms(Platform.HOSTED_ONLY.class)
public static int forInstance(ResolvedJavaType type, int size) {
guaranteeEncoding(type, size > LAST_SPECIAL_VALUE, "Instance type size must be above special values for encoding: " + size);
int encoding = size;
guaranteeEncoding(type, isInstance(encoding), "Instance type encoding must denote an instance");
guaranteeEncoding(type, !isArray(encoding), "Instance type encoding must not denote an array");
guaranteeEncoding(type, !isObjectArray(encoding), "Instance type encoding must not denote an object array");
guaranteeEncoding(type, !isPrimitiveArray(encoding), "Instance type encoding must not denote a primitive array");
guaranteeEncoding(type, getInstanceSize(encoding).equal(WordFactory.unsigned(size)), "Instance type encoding size must match type size");
return encoding;
}
@Platforms(Platform.HOSTED_ONLY.class)
public static int forArray(ResolvedJavaType type, boolean isObject, int arrayBaseOffset, int arrayIndexShift) {
int tag = isObject ? ARRAY_TAG_OBJECT_VALUE : ARRAY_TAG_PRIMITIVE_VALUE;
int encoding = (tag << ARRAY_TAG_SHIFT) | (arrayBaseOffset << ARRAY_BASE_SHIFT) | (arrayIndexShift << ARRAY_INDEX_SHIFT_SHIFT);
guaranteeEncoding(type, isArray(encoding), "Array encoding must denote an array");
guaranteeEncoding(type, !isInstance(encoding), "Array encoding must not denote an instance type");
guaranteeEncoding(type, isObjectArray(encoding) == isObject, "Expected isObjectArray(encoding) == " + isObject);
guaranteeEncoding(type, isPrimitiveArray(encoding) != isObject, "Expected isPrimitiveArray(encoding) != " + isObject);
guaranteeEncoding(type, getArrayBaseOffset(encoding).equal(WordFactory.unsigned(arrayBaseOffset)),
"Expected array base offset of " + arrayBaseOffset + ", but encoding gives " + getArrayBaseOffset(encoding));
guaranteeEncoding(type, getArrayIndexShift(encoding) == arrayIndexShift,
"Expected array index shift of " + arrayIndexShift + ", but encoding gives " + getArrayIndexShift(encoding));
return encoding;
}
public static boolean isPrimitive(int encoding) {
return encoding == PRIMITIVE_VALUE;
}
public static boolean isInterface(int encoding) {
return encoding == INTERFACE_VALUE;
}
public static boolean isAbstract(int encoding) {
return encoding == ABSTRACT_VALUE;
}
public static boolean isInstance(int encoding) {
return encoding > LAST_SPECIAL_VALUE;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getInstanceSize(int encoding) {
return WordFactory.unsigned(encoding);
}
// May be inlined because it does not deal in Pointers.
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isArray(int encoding) {
return encoding < NEUTRAL_VALUE;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isPrimitiveArray(int encoding) {
return UnsignedMath.aboveOrEqual(encoding, ARRAY_TAG_PRIMITIVE_VALUE << ARRAY_TAG_SHIFT);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isObjectArray(int encoding) {
return encoding < (ARRAY_TAG_PRIMITIVE_VALUE << ARRAY_TAG_SHIFT);
}
// May be inlined because it does not deal in Pointers.
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getArrayBaseOffset(int encoding) {
return WordFactory.unsigned((encoding >> ARRAY_BASE_SHIFT) & ARRAY_BASE_MASK);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int getArrayIndexShift(int encoding) {
return (encoding >> ARRAY_INDEX_SHIFT_SHIFT) & ARRAY_INDEX_SHIFT_MASK;
}
public static int getArrayIndexScale(int encoding) {
return 1 << getArrayIndexShift(encoding);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getArrayElementOffset(int encoding, int index) {
return getArrayBaseOffset(encoding).add(WordFactory.unsigned(index).shiftLeft(getArrayIndexShift(encoding)));
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getArraySize(int encoding, int length) {
int alignmentMask = getAlignmentMask();
return getArrayElementOffset(encoding, length).add(alignmentMask).and(~alignmentMask);
}
public static UnsignedWord getSizeFromObject(Object obj) {
int encoding = KnownIntrinsics.readHub(obj).getLayoutEncoding();
if (isArray(encoding)) {
return getArraySize(encoding, ArrayLengthNode.arrayLength(obj));
} else {
return getInstanceSize(encoding);
}
}
Returns the end of the Object when the call started, e.g., for logging. /** Returns the end of the Object when the call started, e.g., for logging. */
public static Pointer getObjectEnd(Object obj) {
// TODO: This assumes that the object starts at obj.
// - In other universes obj could point to the hub in the middle of,
// for example, a butterfly object.
final Pointer objStart = Word.objectToUntrackedPointer(obj);
final UnsignedWord objSize = getSizeFromObject(obj);
return objStart.add(objSize);
}
public static boolean isArray(Object obj) {
final int encoding = KnownIntrinsics.readHub(obj).getLayoutEncoding();
return isArray(encoding);
}
public static boolean isInstance(Object obj) {
final int encoding = KnownIntrinsics.readHub(obj).getLayoutEncoding();
return isInstance(encoding);
}
@Fold
protected static int getAlignmentMask() {
return ImageSingletons.lookup(ObjectLayout.class).getAlignment() - 1;
}
}