use of org.graalvm.compiler.nodes.spi.SimplifierTool in project graal by oracle.
the class ConvertDeoptimizeToGuardPhase method propagateFixed.
@SuppressWarnings("try")
private static void propagateFixed(FixedNode from, StaticDeoptimizingNode deopt, CoreProviders providers, LazyValue<LoopsData> lazyLoops) {
Node current = from;
while (current != null) {
if (GraalOptions.GuardPriorities.getValue(from.getOptions()) && current instanceof FixedGuardNode) {
FixedGuardNode otherGuard = (FixedGuardNode) current;
if (otherGuard.computePriority().isHigherPriorityThan(deopt.computePriority())) {
moveAsDeoptAfter(otherGuard, deopt);
return;
}
} else if (current instanceof AbstractBeginNode) {
if (current instanceof AbstractMergeNode) {
AbstractMergeNode mergeNode = (AbstractMergeNode) current;
FixedNode next = mergeNode.next();
while (mergeNode.isAlive()) {
AbstractEndNode end = mergeNode.forwardEnds().first();
propagateFixed(end, deopt, providers, lazyLoops);
}
if (next.isAlive()) {
propagateFixed(next, deopt, providers, lazyLoops);
}
return;
} else if (current.predecessor() instanceof IfNode) {
AbstractBeginNode begin = (AbstractBeginNode) current;
IfNode ifNode = (IfNode) current.predecessor();
if (isOsrLoopExit(begin) || isCountedLoopExit(ifNode, lazyLoops)) {
moveAsDeoptAfter(begin, deopt);
} else {
// Prioritize the source position of the IfNode
try (DebugCloseable closable = ifNode.withNodeSourcePosition()) {
StructuredGraph graph = ifNode.graph();
LogicNode conditionNode = ifNode.condition();
boolean negateGuardCondition = current == ifNode.trueSuccessor();
NodeSourcePosition survivingSuccessorPosition = negateGuardCondition ? ifNode.falseSuccessor().getNodeSourcePosition() : ifNode.trueSuccessor().getNodeSourcePosition();
FixedGuardNode guard = graph.add(new FixedGuardNode(conditionNode, deopt.getReason(), deopt.getAction(), deopt.getSpeculation(), negateGuardCondition, survivingSuccessorPosition));
FixedWithNextNode pred = (FixedWithNextNode) ifNode.predecessor();
AbstractBeginNode survivingSuccessor;
if (negateGuardCondition) {
survivingSuccessor = ifNode.falseSuccessor();
} else {
survivingSuccessor = ifNode.trueSuccessor();
}
graph.removeSplitPropagate(ifNode, survivingSuccessor);
Node newGuard = guard;
if (survivingSuccessor instanceof LoopExitNode) {
newGuard = ProxyNode.forGuard(guard, (LoopExitNode) survivingSuccessor);
}
survivingSuccessor.replaceAtUsages(newGuard, InputType.Guard);
graph.getDebug().log("Converting deopt on %-5s branch of %s to guard for remaining branch %s.", negateGuardCondition, ifNode, survivingSuccessor);
FixedNode next = pred.next();
pred.setNext(guard);
guard.setNext(next);
assert providers != null;
SimplifierTool simplifierTool = GraphUtil.getDefaultSimplifier(providers, false, graph.getAssumptions(), graph.getOptions());
((Simplifiable) survivingSuccessor).simplify(simplifierTool);
}
}
return;
} else if (current.predecessor() == null || current.predecessor() instanceof ControlSplitNode) {
assert current.predecessor() != null || (current instanceof StartNode && current == ((AbstractBeginNode) current).graph().start());
moveAsDeoptAfter((AbstractBeginNode) current, deopt);
return;
}
}
current = current.predecessor();
}
}
use of org.graalvm.compiler.nodes.spi.SimplifierTool in project graal by oracle.
the class MultiGuardNode method simplify.
@Override
public void simplify(SimplifierTool tool) {
if (usages().filter(node -> node instanceof ValueAnchorNode).isNotEmpty()) {
/*
* For ValueAnchorNode usages, we can optimize MultiGuardNodes away if they depend on
* zero or one floating nodes (as opposed to fixed nodes).
*/
Node singleFloatingGuard = null;
for (ValueNode guard : guards) {
if (GraphUtil.isFloatingNode(guard)) {
if (singleFloatingGuard == null) {
singleFloatingGuard = guard;
} else if (singleFloatingGuard != guard) {
return;
}
}
}
for (Node usage : usages().snapshot()) {
if (usage instanceof ValueAnchorNode) {
usage.replaceFirstInput(this, singleFloatingGuard);
tool.addToWorkList(usage);
}
}
if (usages().isEmpty()) {
GraphUtil.killWithUnusedFloatingInputs(this);
}
}
}
use of org.graalvm.compiler.nodes.spi.SimplifierTool in project graal by oracle.
the class LoopTransformations method fullUnroll.
@SuppressWarnings("try")
public static void fullUnroll(LoopEx loop, CoreProviders context, CanonicalizerPhase canonicalizer) {
// assert loop.isCounted(); //TODO (gd) strengthen : counted with known trip count
LoopBeginNode loopBegin = loop.loopBegin();
StructuredGraph graph = loopBegin.graph();
int initialNodeCount = graph.getNodeCount();
SimplifierTool defaultSimplifier = GraphUtil.getDefaultSimplifier(context, canonicalizer.getCanonicalizeReads(), graph.getAssumptions(), graph.getOptions());
/*
* IMPORTANT: Canonicalizations inside the body of the remaining loop can introduce new
* control flow that is not automatically picked up by the control flow graph computation of
* the original LoopEx data structure, thus we disable simplification and manually simplify
* conditions in the peeled iteration to simplify the exit path.
*/
CanonicalizerPhase c = canonicalizer.copyWithoutSimplification();
EconomicSetNodeEventListener l = new EconomicSetNodeEventListener();
int peelings = 0;
try (NodeEventScope ev = graph.trackNodeEvents(l)) {
while (!loopBegin.isDeleted()) {
Mark newNodes = graph.getMark();
/*
* Mark is not enough for the canonicalization of the floating nodes in the unrolled
* code since pre-existing constants are not new nodes. Therefore, we canonicalize
* (without simplification) all floating nodes changed during peeling but only
* simplify new (in the peeled iteration) ones.
*/
EconomicSetNodeEventListener peeledListener = new EconomicSetNodeEventListener();
try (NodeEventScope peeledScope = graph.trackNodeEvents(peeledListener)) {
LoopTransformations.peel(loop);
}
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After peeling loop %s", loop);
c.applyIncremental(graph, context, peeledListener.getNodes());
loop.invalidateFragmentsAndIVs();
for (Node n : graph.getNewNodes(newNodes)) {
if (n.isAlive() && (n instanceof IfNode || n instanceof SwitchNode || n instanceof FixedGuardNode || n instanceof BeginNode)) {
Simplifiable s = (Simplifiable) n;
s.simplify(defaultSimplifier);
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After simplifying if %s", s);
}
}
if (graph.getNodeCount() > initialNodeCount + MaximumDesiredSize.getValue(graph.getOptions()) * 2 || peelings > DefaultLoopPolicies.Options.FullUnrollMaxIterations.getValue(graph.getOptions())) {
throw new RetryableBailoutException("FullUnroll : Graph seems to grow out of proportion");
}
peelings++;
}
}
// Canonicalize with the original canonicalizer to capture all simplifications
canonicalizer.applyIncremental(graph, context, l.getNodes());
}
use of org.graalvm.compiler.nodes.spi.SimplifierTool in project graal by oracle.
the class IntegerExactOverflowNode method simplify.
@Override
public void simplify(SimplifierTool tool) {
// Find all ifs that this node feeds into
for (IfNode ifNode : usages().filter(IfNode.class).snapshot()) {
// Replace the if with exact split
AbstractBeginNode next = ifNode.falseSuccessor();
AbstractBeginNode overflow = ifNode.trueSuccessor();
ifNode.clearSuccessors();
// Try to find corresponding exact nodes that could be combined with the split. They
// would be directly
// linked to the BeginNode of the false branch.
List<? extends BinaryNode> coupledNodes = next.usages().filter(getCoupledType()).filter(n -> {
BinaryNode exact = (BinaryNode) n;
return exact.getX() == getX() && exact.getY() == getY();
}).snapshot();
Stamp splitStamp = x.stamp(NodeView.DEFAULT).unrestricted();
if (!coupledNodes.isEmpty()) {
splitStamp = coupledNodes.iterator().next().stamp(NodeView.DEFAULT);
}
IntegerExactArithmeticSplitNode split = graph().add(createSplit(splitStamp, next, overflow));
ifNode.replaceAndDelete(split);
coupledNodes.forEach(n -> n.replaceAndDelete(split));
}
}
use of org.graalvm.compiler.nodes.spi.SimplifierTool 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.
* <p>
* 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.hasMoreThanOneUsage()) {
/*
* 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 = getKeyProbabilities()[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 = getKeyProbabilities()[getKeyProbabilities().length - 1];
if (loadIndexed.getBoundsCheck() == null) {
/*
* 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;
}
Aggregations