package org.graalvm.compiler.replacements;
import static org.graalvm.compiler.nodes.calc.CompareNode.createCompareNode;
import java.util.List;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.core.common.calc.Condition;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.ConditionAnchorNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.FixedGuardNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.LogicConstantNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.ShortCircuitOrNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.CompareNode;
import org.graalvm.compiler.nodes.calc.ConditionalNode;
import org.graalvm.compiler.nodes.calc.FloatingNode;
import org.graalvm.compiler.nodes.java.ClassIsAssignableFromNode;
import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode;
import org.graalvm.compiler.nodes.java.InstanceOfNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates;
import org.graalvm.compiler.replacements.SnippetTemplate.Arguments;
import org.graalvm.compiler.replacements.SnippetTemplate.UsageReplacer;
import jdk.vm.ci.code.TargetDescription;
public abstract class InstanceOfSnippetsTemplates extends AbstractTemplates {
public InstanceOfSnippetsTemplates(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
super(providers, snippetReflection, target);
}
protected abstract Arguments makeArguments(InstanceOfUsageReplacer replacer, LoweringTool tool);
public void lower(FloatingNode instanceOf, LoweringTool tool) {
assert instanceOf instanceof InstanceOfNode || instanceOf instanceof InstanceOfDynamicNode || instanceOf instanceof ClassIsAssignableFromNode;
List<Node> usages = instanceOf.usages().snapshot();
Instantiation instantiation = new Instantiation();
for (Node usage : usages) {
final StructuredGraph graph = (StructuredGraph) usage.graph();
InstanceOfUsageReplacer replacer = createReplacer(instanceOf, instantiation, usage, graph);
if (instantiation.isInitialized()) {
replacer.replaceUsingInstantiation();
} else {
Arguments args = makeArguments(replacer, tool);
template(args).instantiate(providers.getMetaAccess(), instanceOf, replacer, tool, args);
}
}
assert instanceOf.hasNoUsages();
if (!instanceOf.isDeleted()) {
GraphUtil.killWithUnusedFloatingInputs(instanceOf);
}
}
protected InstanceOfUsageReplacer createReplacer(FloatingNode instanceOf, Instantiation instantiation, Node usage, final StructuredGraph graph) {
InstanceOfUsageReplacer replacer;
if (!canMaterialize(usage)) {
ValueNode trueValue = ConstantNode.forInt(1, graph);
ValueNode falseValue = ConstantNode.forInt(0, graph);
if (instantiation.isInitialized() && (trueValue != instantiation.trueValue || falseValue != instantiation.falseValue)) {
trueValue = instantiation.trueValue;
falseValue = instantiation.falseValue;
}
replacer = new NonMaterializationUsageReplacer(instantiation, trueValue, falseValue, instanceOf, usage);
} else {
assert usage instanceof ConditionalNode : "unexpected usage of " + instanceOf + ": " + usage;
ConditionalNode c = (ConditionalNode) usage;
replacer = new MaterializationUsageReplacer(instantiation, c.trueValue(), c.falseValue(), instanceOf, c);
}
return replacer;
}
protected boolean canMaterialize(Node usage) {
if (usage instanceof ConditionalNode) {
ConditionalNode cn = (ConditionalNode) usage;
return cn.trueValue().isConstant() && cn.falseValue().isConstant();
}
if (usage instanceof IfNode || usage instanceof FixedGuardNode || usage instanceof ShortCircuitOrNode || usage instanceof ConditionAnchorNode) {
return false;
}
return true;
}
public static final class Instantiation {
private ValueNode result;
private LogicNode condition;
private ValueNode trueValue;
private ValueNode falseValue;
boolean isInitialized() {
return result != null;
}
void initialize(ValueNode r, ValueNode t, ValueNode f) {
assert !isInitialized();
this.result = r;
this.trueValue = t;
this.falseValue = f;
}
LogicNode asCondition(ValueNode testValue) {
assert isInitialized();
if (result.isConstant()) {
assert testValue.isConstant();
return LogicConstantNode.forBoolean(result.asConstant().equals(testValue.asConstant()), result.graph());
}
if (condition == null || (!(condition instanceof CompareNode)) || ((CompareNode) condition).getY() != testValue) {
condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null);
}
return condition;
}
ValueNode asMaterialization(StructuredGraph graph, ValueNode t, ValueNode f) {
assert isInitialized();
if (t == this.trueValue && f == this.falseValue) {
return result;
} else {
return graph.unique(new ConditionalNode(asCondition(trueValue), t, f));
}
}
}
public abstract static class InstanceOfUsageReplacer implements UsageReplacer {
public final Instantiation instantiation;
public final FloatingNode instanceOf;
public final ValueNode trueValue;
public final ValueNode falseValue;
public InstanceOfUsageReplacer(Instantiation instantiation, FloatingNode instanceOf, ValueNode trueValue, ValueNode falseValue) {
assert instanceOf instanceof InstanceOfNode || instanceOf instanceof InstanceOfDynamicNode || instanceOf instanceof ClassIsAssignableFromNode;
this.instantiation = instantiation;
this.instanceOf = instanceOf;
this.trueValue = trueValue;
this.falseValue = falseValue;
}
public abstract void replaceUsingInstantiation();
}
public static class NonMaterializationUsageReplacer extends InstanceOfUsageReplacer {
private final Node usage;
public NonMaterializationUsageReplacer(Instantiation instantiation, ValueNode trueValue, ValueNode falseValue, FloatingNode instanceOf, Node usage) {
super(instantiation, instanceOf, trueValue, falseValue);
this.usage = usage;
}
@Override
public void replaceUsingInstantiation() {
usage.replaceFirstInput(instanceOf, instantiation.asCondition(trueValue));
}
@Override
public void replace(ValueNode oldNode, ValueNode newNode) {
assert newNode instanceof PhiNode;
assert oldNode == instanceOf;
newNode.inferStamp();
instantiation.initialize(newNode, trueValue, falseValue);
usage.replaceFirstInput(oldNode, instantiation.asCondition(trueValue));
}
}
public static class MaterializationUsageReplacer extends InstanceOfUsageReplacer {
public final ConditionalNode usage;
public MaterializationUsageReplacer(Instantiation instantiation, ValueNode trueValue, ValueNode falseValue, FloatingNode instanceOf, ConditionalNode usage) {
super(instantiation, instanceOf, trueValue, falseValue);
this.usage = usage;
}
@Override
public void replaceUsingInstantiation() {
ValueNode newValue = instantiation.asMaterialization(usage.graph(), trueValue, falseValue);
usage.replaceAtUsages(newValue);
assert usage.hasNoUsages();
GraphUtil.killWithUnusedFloatingInputs(usage);
}
@Override
public void replace(ValueNode oldNode, ValueNode newNode) {
assert newNode instanceof PhiNode;
assert oldNode == instanceOf;
newNode.inferStamp();
instantiation.initialize(newNode, trueValue, falseValue);
usage.replaceAtUsages(newNode);
assert usage.hasNoUsages();
GraphUtil.killWithUnusedFloatingInputs(usage);
}
}
}