Search in sources :

Example 6 with LoadIndexedNode

use of org.graalvm.compiler.nodes.java.LoadIndexedNode in project graal by oracle.

the class IntegerSwitchNode method tryOptimizeEnumSwitch.

/**
 * For switch statements on enum values, the Java compiler has to generate complicated code:
 * because {@link Enum#ordinal()} can change when recompiling an enum, it cannot be used
 * directly as the value that is switched on. An intermediate int[] array, which is initialized
 * once at run time based on the actual {@link Enum#ordinal()} values, is used.
 *
 * The {@link ConstantFieldProvider} of Graal already detects the int[] arrays and marks them as
 * {@link ConstantNode#isDefaultStable() stable}, i.e., the array elements are constant. The
 * code in this method detects array loads from such a stable array and re-wires the switch to
 * use the keys from the array elements, so that the array load is unnecessary.
 */
private boolean tryOptimizeEnumSwitch(SimplifierTool tool) {
    if (!(value() instanceof LoadIndexedNode)) {
        /* Not the switch pattern we are looking for. */
        return false;
    }
    LoadIndexedNode loadIndexed = (LoadIndexedNode) value();
    if (loadIndexed.usages().count() > 1) {
        /*
             * The array load is necessary for other reasons too, so there is no benefit optimizing
             * the switch.
             */
        return false;
    }
    assert loadIndexed.usages().first() == this;
    ValueNode newValue = loadIndexed.index();
    JavaConstant arrayConstant = loadIndexed.array().asJavaConstant();
    if (arrayConstant == null || ((ConstantNode) loadIndexed.array()).getStableDimension() != 1 || !((ConstantNode) loadIndexed.array()).isDefaultStable()) {
        /*
             * The array is a constant that we can optimize. We require the array elements to be
             * constant too, since we put them as literal constants into the switch keys.
             */
        return false;
    }
    Integer optionalArrayLength = tool.getConstantReflection().readArrayLength(arrayConstant);
    if (optionalArrayLength == null) {
        /* Loading a constant value can be denied by the VM. */
        return false;
    }
    int arrayLength = optionalArrayLength;
    Map<Integer, List<Integer>> reverseArrayMapping = new HashMap<>();
    for (int i = 0; i < arrayLength; i++) {
        JavaConstant elementConstant = tool.getConstantReflection().readArrayElement(arrayConstant, i);
        if (elementConstant == null || elementConstant.getJavaKind() != JavaKind.Int) {
            /* Loading a constant value can be denied by the VM. */
            return false;
        }
        int element = elementConstant.asInt();
        /*
             * The value loaded from the array is the old switch key, the index into the array is
             * the new switch key. We build a mapping from the old switch key to new keys.
             */
        reverseArrayMapping.computeIfAbsent(element, e -> new ArrayList<>()).add(i);
    }
    /* Build high-level representation of new switch keys. */
    List<KeyData> newKeyDatas = new ArrayList<>(arrayLength);
    ArrayList<AbstractBeginNode> newSuccessors = new ArrayList<>(blockSuccessorCount());
    for (int i = 0; i < keys.length; i++) {
        List<Integer> newKeys = reverseArrayMapping.get(keys[i]);
        if (newKeys == null || newKeys.size() == 0) {
            /* The switch case is unreachable, we can ignore it. */
            continue;
        }
        /*
             * We do not have detailed profiling information about the individual new keys, so we
             * have to assume they split the probability of the old key.
             */
        double newKeyProbability = keyProbabilities[i] / newKeys.size();
        int newKeySuccessor = addNewSuccessor(keySuccessor(i), newSuccessors);
        for (int newKey : newKeys) {
            newKeyDatas.add(new KeyData(newKey, newKeyProbability, newKeySuccessor));
        }
    }
    int newDefaultSuccessor = addNewSuccessor(defaultSuccessor(), newSuccessors);
    double newDefaultProbability = keyProbabilities[keyProbabilities.length - 1];
    /*
         * We remove the array load, but we still need to preserve exception semantics by keeping
         * the bounds check. Fortunately the array length is a constant.
         */
    LogicNode boundsCheck = graph().unique(new IntegerBelowNode(newValue, ConstantNode.forInt(arrayLength, graph())));
    graph().addBeforeFixed(this, graph().add(new FixedGuardNode(boundsCheck, DeoptimizationReason.BoundsCheckException, DeoptimizationAction.InvalidateReprofile)));
    /*
         * Build the low-level representation of the new switch keys and replace ourself with a new
         * node.
         */
    doReplace(newValue, newKeyDatas, newSuccessors, newDefaultSuccessor, newDefaultProbability);
    /* The array load is now unnecessary. */
    assert loadIndexed.hasNoUsages();
    GraphUtil.removeFixedWithUnusedInputs(loadIndexed);
    return true;
}
Also used : DeoptimizationReason(jdk.vm.ci.meta.DeoptimizationReason) Arrays(java.util.Arrays) ConstantNode(org.graalvm.compiler.nodes.ConstantNode) DeoptimizationAction(jdk.vm.ci.meta.DeoptimizationAction) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) NodeLIRBuilderTool(org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool) SimplifierTool(org.graalvm.compiler.graph.spi.SimplifierTool) IntegerStamp(org.graalvm.compiler.core.common.type.IntegerStamp) StampFactory(org.graalvm.compiler.core.common.type.StampFactory) JavaKind(jdk.vm.ci.meta.JavaKind) NodeClass(org.graalvm.compiler.graph.NodeClass) Map(java.util.Map) LoadIndexedNode(org.graalvm.compiler.nodes.java.LoadIndexedNode) IntegerBelowNode(org.graalvm.compiler.nodes.calc.IntegerBelowNode) NodeInfo(org.graalvm.compiler.nodeinfo.NodeInfo) GraphUtil(org.graalvm.compiler.nodes.util.GraphUtil) NodeView(org.graalvm.compiler.nodes.NodeView) AbstractBeginNode(org.graalvm.compiler.nodes.AbstractBeginNode) FixedGuardNode(org.graalvm.compiler.nodes.FixedGuardNode) PrimitiveStamp(org.graalvm.compiler.core.common.type.PrimitiveStamp) Stamp(org.graalvm.compiler.core.common.type.Stamp) LIRLowerable(org.graalvm.compiler.nodes.spi.LIRLowerable) JavaConstant(jdk.vm.ci.meta.JavaConstant) LogicNode(org.graalvm.compiler.nodes.LogicNode) ValueNode(org.graalvm.compiler.nodes.ValueNode) List(java.util.List) ConstantFieldProvider(org.graalvm.compiler.core.common.spi.ConstantFieldProvider) Simplifiable(org.graalvm.compiler.graph.spi.Simplifiable) Comparator(java.util.Comparator) FixedWithNextNode(org.graalvm.compiler.nodes.FixedWithNextNode) IntegerBelowNode(org.graalvm.compiler.nodes.calc.IntegerBelowNode) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) JavaConstant(jdk.vm.ci.meta.JavaConstant) AbstractBeginNode(org.graalvm.compiler.nodes.AbstractBeginNode) FixedGuardNode(org.graalvm.compiler.nodes.FixedGuardNode) ConstantNode(org.graalvm.compiler.nodes.ConstantNode) LoadIndexedNode(org.graalvm.compiler.nodes.java.LoadIndexedNode) ValueNode(org.graalvm.compiler.nodes.ValueNode) ArrayList(java.util.ArrayList) List(java.util.List) LogicNode(org.graalvm.compiler.nodes.LogicNode)

Example 7 with LoadIndexedNode

use of org.graalvm.compiler.nodes.java.LoadIndexedNode in project graal by oracle.

the class GraphUtil method virtualizeArrayCopy.

/**
 * Virtualize an array copy.
 *
 * @param tool the virtualization tool
 * @param source the source array
 * @param sourceLength the length of the source array
 * @param newLength the length of the new array
 * @param from the start index in the source array
 * @param newComponentType the component type of the new array
 * @param elementKind the kind of the new array elements
 * @param graph the node graph
 * @param virtualArrayProvider a functional provider that returns a new virtual array given the
 *            component type and length
 */
public static void virtualizeArrayCopy(VirtualizerTool tool, ValueNode source, ValueNode sourceLength, ValueNode newLength, ValueNode from, ResolvedJavaType newComponentType, JavaKind elementKind, StructuredGraph graph, BiFunction<ResolvedJavaType, Integer, VirtualArrayNode> virtualArrayProvider) {
    ValueNode sourceAlias = tool.getAlias(source);
    ValueNode replacedSourceLength = tool.getAlias(sourceLength);
    ValueNode replacedNewLength = tool.getAlias(newLength);
    ValueNode replacedFrom = tool.getAlias(from);
    if (!replacedNewLength.isConstant() || !replacedFrom.isConstant() || !replacedSourceLength.isConstant()) {
        return;
    }
    assert newComponentType != null : "An array copy can be virtualized only if the real type of the resulting array is known statically.";
    int fromInt = replacedFrom.asJavaConstant().asInt();
    int newLengthInt = replacedNewLength.asJavaConstant().asInt();
    int sourceLengthInt = replacedSourceLength.asJavaConstant().asInt();
    if (sourceAlias instanceof VirtualObjectNode) {
        VirtualObjectNode sourceVirtual = (VirtualObjectNode) sourceAlias;
        assert sourceLengthInt == sourceVirtual.entryCount();
    }
    if (fromInt < 0 || newLengthInt < 0 || fromInt > sourceLengthInt) {
        /* Illegal values for either from index, the new length or the source length. */
        return;
    }
    if (newLengthInt >= tool.getMaximumEntryCount()) {
        /* The new array size is higher than maximum allowed size of virtualized objects. */
        return;
    }
    ValueNode[] newEntryState = new ValueNode[newLengthInt];
    int readLength = Math.min(newLengthInt, sourceLengthInt - fromInt);
    if (sourceAlias instanceof VirtualObjectNode) {
        /* The source array is virtualized, just copy over the values. */
        VirtualObjectNode sourceVirtual = (VirtualObjectNode) sourceAlias;
        for (int i = 0; i < readLength; i++) {
            newEntryState[i] = tool.getEntry(sourceVirtual, fromInt + i);
        }
    } else {
        /* The source array is not virtualized, emit index loads. */
        for (int i = 0; i < readLength; i++) {
            LoadIndexedNode load = new LoadIndexedNode(null, sourceAlias, ConstantNode.forInt(i + fromInt, graph), elementKind);
            tool.addNode(load);
            newEntryState[i] = load;
        }
    }
    if (readLength < newLengthInt) {
        /* Pad the copy with the default value of its elment kind. */
        ValueNode defaultValue = ConstantNode.defaultForKind(elementKind, graph);
        for (int i = readLength; i < newLengthInt; i++) {
            newEntryState[i] = defaultValue;
        }
    }
    /* Perform the replacement. */
    VirtualArrayNode newVirtualArray = virtualArrayProvider.apply(newComponentType, newLengthInt);
    tool.createVirtualObject(newVirtualArray, newEntryState, Collections.<MonitorIdNode>emptyList(), false);
    tool.replaceWithVirtual(newVirtualArray);
}
Also used : VirtualObjectNode(org.graalvm.compiler.nodes.virtual.VirtualObjectNode) LoadIndexedNode(org.graalvm.compiler.nodes.java.LoadIndexedNode) VirtualArrayNode(org.graalvm.compiler.nodes.virtual.VirtualArrayNode) ValueNode(org.graalvm.compiler.nodes.ValueNode)

Aggregations

LoadIndexedNode (org.graalvm.compiler.nodes.java.LoadIndexedNode)7 ValueNode (org.graalvm.compiler.nodes.ValueNode)5 LoadFieldNode (org.graalvm.compiler.nodes.java.LoadFieldNode)3 StoreIndexedNode (org.graalvm.compiler.nodes.java.StoreIndexedNode)3 List (java.util.List)2 ResolvedJavaType (jdk.vm.ci.meta.ResolvedJavaType)2 Node (org.graalvm.compiler.graph.Node)2 AbstractBeginNode (org.graalvm.compiler.nodes.AbstractBeginNode)2 ConstantNode (org.graalvm.compiler.nodes.ConstantNode)2 FixedWithNextNode (org.graalvm.compiler.nodes.FixedWithNextNode)2 IntegerSwitchNode (org.graalvm.compiler.nodes.extended.IntegerSwitchNode)2 ArrayLengthNode (org.graalvm.compiler.nodes.java.ArrayLengthNode)2 StoreFieldNode (org.graalvm.compiler.nodes.java.StoreFieldNode)2 MemoryCheckpoint (org.graalvm.compiler.nodes.memory.MemoryCheckpoint)2 ArrayList (java.util.ArrayList)1 Arrays (java.util.Arrays)1 Comparator (java.util.Comparator)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 DeoptimizationAction (jdk.vm.ci.meta.DeoptimizationAction)1