package com.oracle.svm.hosted.phases;
import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
import org.graalvm.compiler.core.common.type.ObjectStamp;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.core.common.type.TypeReference;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.FixedGuardNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
import org.graalvm.compiler.nodes.LogicConstantNode;
import org.graalvm.compiler.nodes.NodeView;
import org.graalvm.compiler.nodes.ParameterNode;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.PiNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.java.InstanceOfNode;
import org.graalvm.compiler.nodes.java.LoadFieldNode;
import org.graalvm.compiler.nodes.spi.LimitedValueProxy;
import org.graalvm.compiler.phases.Phase;
import com.oracle.svm.hosted.meta.HostedField;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.HostedType;
import jdk.vm.ci.meta.DeoptimizationAction;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaTypeProfile;
import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.TriState;
public class StrengthenStampsPhase extends Phase {
@Override
protected void run(StructuredGraph graph) {
for (Node n : graph.getNodes()) {
if (n instanceof ValueNode && !(n instanceof LimitedValueProxy) && !(n instanceof PhiNode)) {
ValueNode node = (ValueNode) n;
node.inferStamp();
Stamp newStamp = strengthen(node.stamp(NodeView.DEFAULT));
if (newStamp != null) {
node.setStamp(newStamp);
}
}
if (n instanceof LoadFieldNode) {
LoadFieldNode node = (LoadFieldNode) n;
updateStamp(node, toHosted(node.field()).getFieldTypeProfile());
} else if (n instanceof InstanceOfNode) {
InstanceOfNode node = (InstanceOfNode) n;
ObjectStamp newStamp = (ObjectStamp) strengthen(node.getCheckedStamp());
if (newStamp != null) {
node.replaceAndDelete(graph.addOrUniqueWithInputs(InstanceOfNode.createHelper(newStamp, node.getValue(), node.profile(), node.getAnchor())));
}
} else if (n instanceof PiNode) {
PiNode node = (PiNode) n;
Stamp newStamp = strengthen(node.piStamp());
if (newStamp != null) {
node.strengthenPiStamp(newStamp);
}
}
}
}
private Stamp strengthen(Stamp s) {
if (!(s instanceof AbstractObjectStamp)) {
return null;
}
AbstractObjectStamp stamp = (AbstractObjectStamp) s;
HostedType originalType = toHosted(stamp.type());
if (originalType == null) {
return null;
}
HostedType strengthenType = originalType.getStrengthenStampType();
if (originalType.equals(strengthenType)) {
return null;
}
Stamp newStamp;
if (strengthenType == null) {
if (stamp.nonNull()) {
newStamp = StampFactory.empty(JavaKind.Object);
} else {
newStamp = StampFactory.alwaysNull();
}
} else {
if (stamp.isExactType()) {
newStamp = StampFactory.empty(JavaKind.Object);
} else {
ResolvedJavaType targetType = toTarget(strengthenType);
if (targetType == null) {
return null;
}
TypeReference typeRef = TypeReference.createTrustedWithoutAssumptions(targetType);
newStamp = StampFactory.object(typeRef, stamp.nonNull());
}
}
return newStamp;
}
private void updateStamp(ValueNode node, JavaTypeProfile typeProfile) {
if (node.getStackKind() != JavaKind.Object) {
return;
}
if (typeProfile != null) {
Stamp newStamp = strengthenStamp(node, typeProfile);
if (!newStamp.equals(node.stamp(NodeView.DEFAULT))) {
node.getDebug().log("STAMP UPDATE method %s node %s old %s new %s\n", node.graph().method().format("%H.%n(%p)"), node, node.stamp(NodeView.DEFAULT), newStamp);
node.setStamp(newStamp);
}
}
}
private Stamp strengthenStamp(ValueNode node, JavaTypeProfile typeProfile) {
ObjectStamp oldStamp = (ObjectStamp) node.stamp(NodeView.DEFAULT);
HostedType oldType = toHosted(oldStamp.type());
if (oldStamp.alwaysNull()) {
return oldStamp;
}
boolean nonNull = oldStamp.nonNull() || typeProfile.getNullSeen() == TriState.FALSE;
ProfiledType[] exactTypes = typeProfile.getTypes();
if (exactTypes.length == 1) {
ResolvedJavaType exactType = exactTypes[0].getType();
assert oldType == null || oldType.isAssignableFrom(exactType);
if (!oldStamp.isExactType() || !exactType.equals(oldType) || nonNull != oldStamp.nonNull()) {
ResolvedJavaType targetType = toTarget(exactType);
if (targetType == null) {
return oldStamp;
}
TypeReference typeRef = TypeReference.createExactTrusted(targetType);
return nonNull ? StampFactory.objectNonNull(typeRef) : StampFactory.object(typeRef);
} else {
return oldStamp;
}
}
if (exactTypes.length == 0) {
if (!nonNull) {
return StampFactory.alwaysNull();
} else {
StructuredGraph graph = node.graph();
FixedWithNextNode insertionPoint;
if (node instanceof ParameterNode) {
insertionPoint = graph.start();
} else if (node instanceof InvokeWithExceptionNode) {
insertionPoint = ((InvokeWithExceptionNode) node).next();
} else {
insertionPoint = (FixedWithNextNode) node;
}
graph.addAfterFixed(insertionPoint, graph.add(new FixedGuardNode(LogicConstantNode.forBoolean(true, graph), DeoptimizationReason.UnreachedCode, DeoptimizationAction.None, true)));
return oldStamp;
}
}
ResolvedJavaType baseType;
if (oldStamp.isExactType()) {
baseType = oldType;
} else {
assert exactTypes.length > 1;
assert oldType == null || oldType.isAssignableFrom(exactTypes[0].getType());
baseType = exactTypes[0].getType();
for (int i = 1; i < exactTypes.length; i++) {
assert oldType == null || oldType.isAssignableFrom(exactTypes[i].getType());
baseType = baseType.findLeastCommonAncestor(exactTypes[i].getType());
}
if (oldType != null && !oldType.isAssignableFrom(baseType)) {
baseType = oldType;
}
}
if (!baseType.equals(oldType) || nonNull != oldStamp.nonNull()) {
ResolvedJavaType targetType = toTarget(baseType);
if (targetType == null) {
return oldStamp;
}
TypeReference typeRef = TypeReference.createTrustedWithoutAssumptions(targetType);
return nonNull ? StampFactory.objectNonNull(typeRef) : StampFactory.object(typeRef);
}
return oldStamp;
}
protected HostedType toHosted(ResolvedJavaType type) {
return (HostedType) type;
}
protected HostedMethod toHosted(ResolvedJavaMethod method) {
return (HostedMethod) method;
}
protected HostedField toHosted(ResolvedJavaField field) {
return (HostedField) field;
}
protected ResolvedJavaType toTarget(ResolvedJavaType type) {
return type;
}
}