package org.graalvm.compiler.nodes.java;
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.graph.Node.NodeIntrinsicFactory;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.graph.spi.Canonicalizable;
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.DeoptimizeNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.NodeView;
import org.graalvm.compiler.nodes.PiNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.spi.ArrayLengthProvider;
import org.graalvm.compiler.nodes.spi.Lowerable;
import org.graalvm.compiler.nodes.spi.Virtualizable;
import org.graalvm.compiler.nodes.spi.VirtualizerTool;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.nodes.virtual.VirtualArrayNode;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.DeoptimizationAction;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaKind;
@NodeInfo(cycles = CYCLES_2, size = SIZE_1)
@NodeIntrinsicFactory
public final class ArrayLengthNode extends FixedWithNextNode implements Canonicalizable.Unary<ValueNode>, Lowerable, Virtualizable {
public static final NodeClass<ArrayLengthNode> TYPE = NodeClass.create(ArrayLengthNode.class);
@Input ValueNode array;
public ValueNode array() {
return array;
}
@Override
public ValueNode getValue() {
return array;
}
public ArrayLengthNode(ValueNode array) {
super(TYPE, StampFactory.positiveInt());
this.array = array;
}
public static ValueNode create(ValueNode forValue, ConstantReflectionProvider constantReflection) {
if (forValue instanceof NewArrayNode) {
NewArrayNode newArray = (NewArrayNode) forValue;
return newArray.length();
}
ValueNode length = readArrayLength(forValue, constantReflection);
if (length != null) {
return length;
}
return new ArrayLengthNode(forValue);
}
@Override
public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
if (forValue.isNullConstant()) {
return new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException);
}
ValueNode length = readArrayLength(forValue, tool.getConstantReflection());
if (length != null) {
return length;
}
return this;
}
public static ValueNode readArrayLength(ValueNode originalArray, ConstantReflectionProvider constantReflection) {
return GraphUtil.arrayLength(originalArray, ArrayLengthProvider.FindLengthMode.CANONICALIZE_READ, constantReflection);
}
public static boolean intrinsify(GraphBuilderContext b, ValueNode array) {
ValueNode anchoredArray;
AbstractObjectStamp arrayStamp = (AbstractObjectStamp) array.stamp(NodeView.DEFAULT);
if (!arrayStamp.isAlwaysArray() || !arrayStamp.nonNull()) {
anchoredArray = b.add(new PiNode(array, arrayStamp.asAlwaysArray().asNonNull(), b.add(new BeginNode())));
} else {
anchoredArray = array;
}
b.addPush(JavaKind.Int, new ArrayLengthNode(anchoredArray));
return true;
}
@NodeIntrinsic
public static native int arrayLength(Object array);
@Override
public void virtualize(VirtualizerTool tool) {
ValueNode alias = tool.getAlias(array());
if (alias instanceof VirtualArrayNode) {
VirtualArrayNode virtualArray = (VirtualArrayNode) alias;
tool.replaceWithValue(ConstantNode.forInt(virtualArray.entryCount(), graph()));
}
}
}