All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdk.graal.compiler.nodes.util.ConstantReflectionUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020, 2024, 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 jdk.graal.compiler.nodes.util;

import java.nio.ByteOrder;

import jdk.graal.compiler.core.common.GraalOptions;
import jdk.graal.compiler.core.common.Stride;
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.nodes.ConstantNode;
import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.spi.CanonicalizerTool;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaType;

/**
 * Helper functions to access complex objects on the application heap via the
 * {@link ConstantReflectionProvider}.
 */
public final class ConstantReflectionUtil {
    private ConstantReflectionUtil() {
    }

    /**
     * Checks if the given value is a {@link JavaConstant} with at least one
     * {@link ConstantNode#getStableDimension() stable dimension}.
     */
    public static boolean isStableJavaArray(ValueNode array) {
        return array.isJavaConstant() && ((ConstantNode) array).getStableDimension() > 0;
    }

    public static int[] loadIntArrayConstant(ConstantReflectionProvider crp, JavaConstant targetArg, int maxLength) {
        int targetArgLength = Integer.min(maxLength, crp.readArrayLength(targetArg));
        int[] targetCharArray = new int[targetArgLength];
        for (int i = 0; i < targetArgLength; i++) {
            targetCharArray[i] = (char) crp.readArrayElement(targetArg, i).asInt();
        }
        return targetCharArray;
    }

    public static boolean shouldConstantFoldArrayOperation(CanonicalizerTool tool, int arrayLength) {
        // constant-folding huge arrays takes too long, do it only for reasonably small arrays
        return arrayLength < GraalOptions.StringIndexOfConstantLimit.getValue(tool.getOptions());
    }

    /**
     * Performs a type punned array bounds check on the given type punned array properties vs actual
     * array properties. A type punned array access is an array access whose bit width not the same
     * as the array element's bit width, e.g. reading a {@code char} value from a {@code byte}
     * array.
     *
     * @param typePunnedOffset array offset in relation to the type punned element size, e.g.
     *            "number of char elements in this byte array".
     * @param typePunnedLength array length in relation to the type punned element size, e.g.
     *            "number of char elements in this byte array".
     * @param typePunnedStride type punned array element size.
     * @param arrayLength actual array length, e.g. actual byte size of the {@code byte} array we
     *            want to read a {@code char} from.
     * @param arrayKind actual array kind, e.g. {@link JavaKind#Byte} for a {@code byte} array we
     *            want to read a {@code char} from.
     */
    public static boolean boundsCheckTypePunned(long typePunnedOffset, int typePunnedLength, Stride typePunnedStride, int arrayLength, JavaKind arrayKind) {
        /*
         * Note: A simple (offset + length |<=| array.length) check does not protect against illegal
         * negative offset or length values for which (offset + length) might appear to be in bounds
         * while the start of the range is out of bounds or greater than the end.
         */
        long arrayByteLength = (long) arrayLength * arrayKind.getByteCount();
        long byteOffset = typePunnedOffset * typePunnedStride.value;
        long byteLength = (long) typePunnedLength * typePunnedStride.value;
        return Long.compareUnsigned(byteOffset + byteLength, arrayByteLength) <= 0 && byteOffset >= 0 && byteLength >= 0;
    }

    /**
     * Reads and zero extends 1, 2 or 4 bytes from a constant byte[], char[] or int[] array.
     *
     * @param array a constant java byte[], char[] or int[] array.
     * @param arrayKind the array's element kind ({@link JavaKind#Byte} for byte[],
     *            {@link JavaKind#Char} for char[] and {@link JavaKind#Int} for int[]).
     * @param stride the element width to read from the array. The stride must not be smaller than
     *            the array element kind, e.g. reading a byte from an int[] array is not supported.
     * @param index the index to read from, scaled to {@code stride}.
     * @return the bytes read, zero-extended to an int value.
     */
    public static int readTypePunned(ConstantReflectionProvider provider, JavaConstant array, JavaKind arrayKind, Stride stride, int index) {
        assert arrayKind == JavaKind.Byte || arrayKind == JavaKind.Char || arrayKind == JavaKind.Int : arrayKind;
        assert stride.value <= 4 : stride.value;
        assert stride.value >= arrayKind.getByteCount() : Assertions.errorMessageContext("stride.val", stride.value, "arrayKind", arrayKind);
        if (arrayKind == JavaKind.Byte) {
            int i = index * stride.value;
            if (stride == Stride.S1) {
                return provider.readArrayElement(array, i).asInt() & 0xff;
            }
            if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
                if (stride == Stride.S2) {
                    return (provider.readArrayElement(array, i).asInt() & 0xff) |
                                    ((provider.readArrayElement(array, i + 1).asInt() & 0xff) << 8);
                } else {
                    return (provider.readArrayElement(array, i).asInt() & 0xff) |
                                    ((provider.readArrayElement(array, i + 1).asInt() & 0xff) << 8) |
                                    ((provider.readArrayElement(array, i + 2).asInt() & 0xff) << 16) |
                                    ((provider.readArrayElement(array, i + 3).asInt() & 0xff) << 24);
                }
            } else {
                if (stride == Stride.S2) {
                    return (provider.readArrayElement(array, i + 1).asInt() & 0xff) |
                                    ((provider.readArrayElement(array, i).asInt() & 0xff) << 8);
                } else {
                    return (provider.readArrayElement(array, i + 3).asInt() & 0xff) |
                                    ((provider.readArrayElement(array, i + 2).asInt() & 0xff) << 8) |
                                    ((provider.readArrayElement(array, i + 1).asInt() & 0xff) << 16) |
                                    ((provider.readArrayElement(array, i).asInt() & 0xff) << 24);
                }
            }
        } else if (arrayKind == JavaKind.Char) {
            if (stride == Stride.S2) {
                return provider.readArrayElement(array, index).asInt() & 0xffff;
            } else {
                assert stride == Stride.S4 : Assertions.errorMessage(stride);
                int i = index * 2;
                if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
                    return (provider.readArrayElement(array, i).asInt() & 0xffff) |
                                    ((provider.readArrayElement(array, i + 1).asInt() & 0xffff) << 16);
                } else {
                    return (provider.readArrayElement(array, i + 1).asInt() & 0xffff) |
                                    ((provider.readArrayElement(array, i).asInt() & 0xffff) << 16);
                }
            }
        } else {
            assert stride == Stride.S4 : stride;
            return provider.readArrayElement(array, index).asInt();
        }
    }

    public interface ArrayBaseOffsetProvider {
        int getArrayBaseOffset(MetaAccessProvider metaAccess, ValueNode array, JavaKind arrayKind);
    }

    /**
     * Return {@code true} if the array and the starting offset are constants, and we can perform
     * {@code len} in-bounds constant reads starting at byte offset {@code offset}. Return
     * {@code false} if not everything constant or if we would try to read out of bounds.
     */
    public static boolean canFoldReads(CanonicalizerTool tool, ValueNode array, ValueNode offset, Stride stride, int len, ArrayBaseOffsetProvider arrayBaseOffsetProvider) {
        if (array.isJavaConstant() && ((ConstantNode) array).getStableDimension() >= 1 && offset.isJavaConstant()) {
            ResolvedJavaType arrayType = array.stamp(NodeView.DEFAULT).javaType(tool.getMetaAccess());
            if (arrayType.isArray()) {
                Integer arrayLength = tool.getConstantReflection().readArrayLength(array.asJavaConstant());
                Integer index = startIndex(tool, array, offset.asJavaConstant(), stride, arrayBaseOffsetProvider);
                return arrayLength != null && index != null && index >= 0 && index + len <= arrayLength;
            }
        }
        return false;
    }

    /**
     * Compute an element index from a byte offset from the start of the array object. Returns
     * {@code null} if the given offset is not aligned correctly for the element kind's stride.
     */
    public static Integer startIndex(CanonicalizerTool tool, ValueNode array, JavaConstant offset, Stride stride, ArrayBaseOffsetProvider arrayBaseOffsetProvider) {
        JavaKind arrayKind = array.stamp(NodeView.DEFAULT).javaType(tool.getMetaAccess()).getComponentType().getJavaKind();
        long elementOffset = offset.asLong() - arrayBaseOffsetProvider.getArrayBaseOffset(tool.getMetaAccess(), array, arrayKind);
        if (elementOffset % stride.value != 0) {
            return null;
        }
        return (int) (elementOffset / stride.value);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy