/*
 * Copyright (c) 2015, 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.heap;

import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader;
import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.annotate.AlwaysInline;
import com.oracle.svm.core.annotate.DuplicatedInNativeCode;
import com.oracle.svm.core.c.NonmovableArray;
import com.oracle.svm.core.code.CodeInfoQueryResult;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.util.NonmovableByteArrayReader;

@DuplicatedInNativeCode
public class CodeReferenceMapDecoder {

    
Walk the reference map encoding from a Pointer, applying a visitor to each Object reference.
Params:
  • baseAddress – A Pointer to a collections of primitives and Object references.
  • referenceMapEncoding – The encoding for the Object references in the collection.
  • referenceMapIndex – The start index for the particular reference map in the encoding.
  • visitor – The visitor to be applied to each Object reference.
Returns:false if any of the visits returned false, true otherwise.
/** * Walk the reference map encoding from a Pointer, applying a visitor to each Object reference. * * @param baseAddress A Pointer to a collections of primitives and Object references. * @param referenceMapEncoding The encoding for the Object references in the collection. * @param referenceMapIndex The start index for the particular reference map in the encoding. * @param visitor The visitor to be applied to each Object reference. * @return false if any of the visits returned false, true otherwise. */
@AlwaysInline("de-virtualize calls to ObjectReferenceVisitor") public static boolean walkOffsetsFromPointer(PointerBase baseAddress, NonmovableArray<Byte> referenceMapEncoding, long referenceMapIndex, ObjectReferenceVisitor visitor) { assert referenceMapIndex != CodeInfoQueryResult.NO_REFERENCE_MAP; assert referenceMapEncoding.isNonNull(); UnsignedWord uncompressedSize = WordFactory.unsigned(FrameAccess.uncompressedReferenceSize()); UnsignedWord compressedSize = WordFactory.unsigned(ConfigurationValues.getObjectLayout().getReferenceSize()); Pointer objRef = (Pointer) baseAddress; long idx = referenceMapIndex; boolean firstRun = true; while (true) { /* * The following code is copied from TypeReader.getUV() and .getSV() because we cannot * allocate a TypeReader here which, in addition to returning the read variable-sized * values, can keep track of the index in the byte array. Even with an instance of * ReusableTypeReader, we would need to worry about this method being reentrant. */ // Size of gap in bytes (negative means the next pointer has derived pointers) long gap = NonmovableByteArrayReader.getU1(referenceMapEncoding, idx++); if (gap >= UnsafeArrayTypeWriter.NUM_LOW_CODES) { long shift = UnsafeArrayTypeWriter.HIGH_WORD_SHIFT; for (int i = 2;; i++) { long b = NonmovableByteArrayReader.getU1(referenceMapEncoding, idx++); gap += b << shift; if (b < UnsafeArrayTypeWriter.NUM_LOW_CODES || i == UnsafeArrayTypeWriter.MAX_BYTES) { break; } shift += UnsafeArrayTypeWriter.HIGH_WORD_SHIFT; } } gap = UnsafeArrayTypeReader.decodeSign(gap); // Number of pointers (sign distinguishes between compression and uncompression) long count = NonmovableByteArrayReader.getU1(referenceMapEncoding, idx++); if (count >= UnsafeArrayTypeWriter.NUM_LOW_CODES) { long shift = UnsafeArrayTypeWriter.HIGH_WORD_SHIFT; for (int i = 2;; i++) { long b = NonmovableByteArrayReader.getU1(referenceMapEncoding, idx++); count += b << shift; if (b < UnsafeArrayTypeWriter.NUM_LOW_CODES || i == UnsafeArrayTypeWriter.MAX_BYTES) { break; } shift += UnsafeArrayTypeWriter.HIGH_WORD_SHIFT; } } count = UnsafeArrayTypeReader.decodeSign(count); if (gap == 0 && count == 0) { break; // reached end of table } boolean derived = false; if (!firstRun && gap < 0) { /* Derived pointer run */ gap = -(gap + 1); derived = true; } firstRun = false; objRef = objRef.add(WordFactory.unsigned(gap)); boolean compressed = (count < 0); UnsignedWord refSize = compressed ? compressedSize : uncompressedSize; count = (count < 0) ? -count : count; if (derived) { /* * To correctly relocate a derived pointer, we need to know the value pointed to by * the base reference and the derived reference before either one is relocated. This * allows us to compute the inner offset, i.e. how much into the actual object does * the derived reference point to. */ Pointer basePtr = baseAddress.isNull() ? objRef : objRef.readWord(0); final boolean visitResult = visitor.visitObjectReferenceInline(objRef, 0, compressed); if (!visitResult) { return false; } /* count in this case is the number of derived references for this base pointer */ for (long d = 0; d < count; d++) { /* Offset in words from the base reference to the derived reference */ long refOffset = NonmovableByteArrayReader.getU1(referenceMapEncoding, idx++); if (refOffset >= UnsafeArrayTypeWriter.NUM_LOW_CODES) { long shift = UnsafeArrayTypeWriter.HIGH_WORD_SHIFT; for (int i = 2;; i++) { long b = NonmovableByteArrayReader.getU1(referenceMapEncoding, idx++); refOffset += b << shift; if (b < UnsafeArrayTypeWriter.NUM_LOW_CODES || i == UnsafeArrayTypeWriter.MAX_BYTES) { break; } shift += UnsafeArrayTypeWriter.HIGH_WORD_SHIFT; } } refOffset = UnsafeArrayTypeReader.decodeSign(refOffset); Pointer derivedRef; if (refOffset >= 0) { derivedRef = objRef.add(WordFactory.unsigned(refOffset).multiply(refSize)); } else { derivedRef = objRef.subtract(WordFactory.unsigned(-refOffset).multiply(refSize)); } Pointer derivedPtr = baseAddress.isNull() ? derivedRef : derivedRef.readWord(0); int innerOffset = NumUtil.safeToInt(derivedPtr.subtract(basePtr).rawValue()); final boolean derivedVisitResult = visitor.visitObjectReferenceInline(derivedRef, innerOffset, compressed); if (!derivedVisitResult) { return false; } } objRef = objRef.add(refSize); } else { for (long c = 0; c < count; c += 1) { final boolean visitResult = visitor.visitObjectReferenceInline(objRef, 0, compressed); if (!visitResult) { return false; } objRef = objRef.add(refSize); } } } return true; } }