001/*
002 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.phases.common;
024
025import java.util.*;
026
027import com.oracle.graal.debug.*;
028import com.oracle.graal.debug.Debug.*;
029import jdk.internal.jvmci.meta.*;
030
031import com.oracle.graal.compiler.common.type.*;
032import com.oracle.graal.graph.*;
033import com.oracle.graal.nodes.*;
034import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
035import com.oracle.graal.nodes.calc.*;
036import com.oracle.graal.nodes.extended.*;
037import com.oracle.graal.nodes.java.*;
038import com.oracle.graal.nodes.spi.*;
039import com.oracle.graal.nodes.type.*;
040import com.oracle.graal.nodes.util.*;
041import com.oracle.graal.phases.*;
042import com.oracle.graal.phases.graph.*;
043
044public class ConditionalEliminationPhase extends Phase {
045
046    private static final DebugMetric metricConditionRegistered = Debug.metric("ConditionRegistered");
047    private static final DebugMetric metricTypeRegistered = Debug.metric("TypeRegistered");
048    private static final DebugMetric metricNullnessRegistered = Debug.metric("NullnessRegistered");
049    private static final DebugMetric metricObjectEqualsRegistered = Debug.metric("ObjectEqualsRegistered");
050    private static final DebugMetric metricCheckCastRemoved = Debug.metric("CheckCastRemoved");
051    private static final DebugMetric metricInstanceOfRemoved = Debug.metric("InstanceOfRemoved");
052    private static final DebugMetric metricNullCheckRemoved = Debug.metric("NullCheckRemoved");
053    private static final DebugMetric metricObjectEqualsRemoved = Debug.metric("ObjectEqualsRemoved");
054    private static final DebugMetric metricGuardsRemoved = Debug.metric("GuardsRemoved");
055
056    private StructuredGraph graph;
057
058    public ConditionalEliminationPhase() {
059    }
060
061    @Override
062    protected void run(StructuredGraph inputGraph) {
063        graph = inputGraph;
064        try (Scope s = Debug.scope("ConditionalElimination")) {
065            new ConditionalElimination(graph.start(), new State()).apply();
066        } catch (Throwable e) {
067            throw Debug.handle(e);
068        }
069    }
070
071    /**
072     * Type information about a {@code value} that it produced by a {@code guard}. Usage of the
073     * stamp information requires adopting the guard. Usually this means replacing an existing guard
074     * with this guard.
075     */
076    static class GuardedStamp {
077        private final ValueNode value;
078        private final Stamp stamp;
079        private final GuardNode guard;
080
081        GuardedStamp(ValueNode value, Stamp stamp, GuardNode guard) {
082            this.value = value;
083            this.stamp = stamp;
084            this.guard = guard;
085        }
086
087        public Stamp getStamp() {
088            return stamp;
089        }
090
091        public GuardNode getGuard() {
092            return guard;
093        }
094
095        public ValueNode getValue() {
096            return value;
097        }
098    }
099
100    public static class State extends MergeableState<State> implements Cloneable {
101
102        private Map<ValueNode, ResolvedJavaType> knownTypes;
103        private Set<ValueNode> knownNonNull;
104        private Set<ValueNode> knownNull;
105        private Map<LogicNode, GuardingNode> trueConditions;
106        private Map<LogicNode, GuardingNode> falseConditions;
107        private Map<ValueNode, GuardedStamp> valueConstraints;
108
109        public State() {
110            this.knownTypes = Node.newIdentityMap();
111            this.knownNonNull = Node.newSet();
112            this.knownNull = Node.newSet();
113            this.trueConditions = Node.newIdentityMap();
114            this.falseConditions = Node.newIdentityMap();
115            this.valueConstraints = Node.newIdentityMap();
116        }
117
118        public State(State other) {
119            this.knownTypes = Node.newIdentityMap(other.knownTypes);
120            this.knownNonNull = Node.newSet(other.knownNonNull);
121            this.knownNull = Node.newSet(other.knownNull);
122            this.trueConditions = Node.newIdentityMap(other.trueConditions);
123            this.falseConditions = Node.newIdentityMap(other.falseConditions);
124            this.valueConstraints = Node.newIdentityMap(other.valueConstraints);
125        }
126
127        @Override
128        public boolean merge(AbstractMergeNode merge, List<State> withStates) {
129            Map<ValueNode, ResolvedJavaType> newKnownTypes = Node.newIdentityMap();
130            Map<LogicNode, GuardingNode> newTrueConditions = Node.newIdentityMap();
131            Map<LogicNode, GuardingNode> newFalseConditions = Node.newIdentityMap();
132            Map<ValueNode, GuardedStamp> newValueConstraints = Node.newIdentityMap();
133
134            Set<ValueNode> newKnownNull = Node.newSet(knownNull);
135            Set<ValueNode> newKnownNonNull = Node.newSet(knownNonNull);
136            for (State state : withStates) {
137                newKnownNull.retainAll(state.knownNull);
138                newKnownNonNull.retainAll(state.knownNonNull);
139            }
140
141            for (Map.Entry<ValueNode, ResolvedJavaType> entry : knownTypes.entrySet()) {
142                ValueNode node = entry.getKey();
143                ResolvedJavaType type = entry.getValue();
144
145                for (State other : withStates) {
146                    ResolvedJavaType otherType = other.getNodeType(node);
147                    type = widen(type, otherType);
148                    if (type == null) {
149                        break;
150                    }
151                }
152                if (type != null && !type.equals(StampTool.typeOrNull(node))) {
153                    newKnownTypes.put(node, type);
154                }
155            }
156
157            for (Map.Entry<LogicNode, GuardingNode> entry : trueConditions.entrySet()) {
158                LogicNode check = entry.getKey();
159                GuardingNode guard = entry.getValue();
160
161                for (State other : withStates) {
162                    GuardingNode otherGuard = other.trueConditions.get(check);
163                    if (otherGuard == null) {
164                        guard = null;
165                        break;
166                    }
167                    if (otherGuard != guard) {
168                        guard = merge;
169                    }
170                }
171                if (guard != null) {
172                    newTrueConditions.put(check, guard);
173                }
174            }
175            for (Map.Entry<LogicNode, GuardingNode> entry : falseConditions.entrySet()) {
176                LogicNode check = entry.getKey();
177                GuardingNode guard = entry.getValue();
178
179                for (State other : withStates) {
180                    GuardingNode otherGuard = other.falseConditions.get(check);
181                    if (otherGuard == null) {
182                        guard = null;
183                        break;
184                    }
185                    if (otherGuard != guard) {
186                        guard = merge;
187                    }
188                }
189                if (guard != null) {
190                    newFalseConditions.put(check, guard);
191                }
192            }
193
194            // this piece of code handles phis
195            if (!(merge instanceof LoopBeginNode)) {
196                for (PhiNode phi : merge.phis()) {
197                    if (phi instanceof ValuePhiNode && phi.getKind() == Kind.Object) {
198                        ValueNode firstValue = phi.valueAt(0);
199                        ResolvedJavaType type = getNodeType(firstValue);
200                        boolean nonNull = knownNonNull.contains(firstValue);
201                        boolean isNull = knownNull.contains(firstValue);
202
203                        for (int i = 0; i < withStates.size(); i++) {
204                            State otherState = withStates.get(i);
205                            ValueNode value = phi.valueAt(i + 1);
206                            ResolvedJavaType otherType = otherState.getNodeType(value);
207                            type = widen(type, otherType);
208                            nonNull &= otherState.knownNonNull.contains(value);
209                            isNull &= otherState.knownNull.contains(value);
210                        }
211                        if (type != null) {
212                            newKnownTypes.put(phi, type);
213                        }
214                        if (nonNull) {
215                            newKnownNonNull.add(phi);
216                        }
217                        if (isNull) {
218                            newKnownNull.add(phi);
219                        }
220                    }
221                }
222            }
223
224            this.knownTypes = newKnownTypes;
225            this.knownNonNull = newKnownNonNull;
226            this.knownNull = newKnownNull;
227            this.trueConditions = newTrueConditions;
228            this.falseConditions = newFalseConditions;
229            this.valueConstraints = newValueConstraints;
230            return true;
231        }
232
233        public ResolvedJavaType getNodeType(ValueNode node) {
234            ResolvedJavaType result = knownTypes.get(GraphUtil.unproxify(node));
235            return result == null ? StampTool.typeOrNull(node) : result;
236        }
237
238        public boolean isNull(ValueNode value) {
239            return StampTool.isPointerAlwaysNull(value) || knownNull.contains(GraphUtil.unproxify(value));
240        }
241
242        public boolean isNonNull(ValueNode value) {
243            return StampTool.isPointerNonNull(value) || knownNonNull.contains(GraphUtil.unproxify(value));
244        }
245
246        @Override
247        public State clone() {
248            return new State(this);
249        }
250
251        /**
252         * Adds information about a condition. If isTrue is true then the condition is known to
253         * hold, otherwise the condition is known not to hold.
254         */
255        public void addCondition(boolean isTrue, LogicNode condition, GuardingNode anchor) {
256            if (isTrue) {
257                if (!trueConditions.containsKey(condition)) {
258                    trueConditions.put(condition, anchor);
259                    metricConditionRegistered.increment();
260                }
261            } else {
262                if (!falseConditions.containsKey(condition)) {
263                    falseConditions.put(condition, anchor);
264                    metricConditionRegistered.increment();
265                }
266            }
267        }
268
269        /**
270         * Adds information about the nullness of a value. If isNull is true then the value is known
271         * to be null, otherwise the value is known to be non-null.
272         */
273        public void addNullness(boolean isNull, ValueNode value) {
274            if (isNull) {
275                if (!isNull(value)) {
276                    metricNullnessRegistered.increment();
277                    knownNull.add(GraphUtil.unproxify(value));
278                }
279            } else {
280                if (!isNonNull(value)) {
281                    metricNullnessRegistered.increment();
282                    knownNonNull.add(GraphUtil.unproxify(value));
283                }
284            }
285        }
286
287        public void addType(ResolvedJavaType type, ValueNode value) {
288            ValueNode original = GraphUtil.unproxify(value);
289            ResolvedJavaType knownType = getNodeType(original);
290            ResolvedJavaType newType = tighten(type, knownType);
291
292            if (!newType.equals(knownType)) {
293                knownTypes.put(original, newType);
294                metricTypeRegistered.increment();
295            }
296        }
297
298        public void clear() {
299            knownTypes.clear();
300            knownNonNull.clear();
301            knownNull.clear();
302            trueConditions.clear();
303            falseConditions.clear();
304        }
305    }
306
307    public static ResolvedJavaType widen(ResolvedJavaType a, ResolvedJavaType b) {
308        if (a == null || b == null) {
309            return null;
310        } else if (a.equals(b)) {
311            return a;
312        } else {
313            return a.findLeastCommonAncestor(b);
314        }
315    }
316
317    public static ResolvedJavaType tighten(ResolvedJavaType a, ResolvedJavaType b) {
318        if (a == null) {
319            return b;
320        } else if (b == null) {
321            return a;
322        } else if (a.equals(b)) {
323            return a;
324        } else if (a.isAssignableFrom(b)) {
325            return b;
326        } else {
327            return a;
328        }
329    }
330
331    public class ConditionalElimination extends SinglePassNodeIterator<State> {
332
333        private final LogicNode trueConstant;
334        private final LogicNode falseConstant;
335
336        public ConditionalElimination(StartNode start, State initialState) {
337            super(start, initialState);
338            trueConstant = LogicConstantNode.tautology(graph);
339            falseConstant = LogicConstantNode.contradiction(graph);
340        }
341
342        @Override
343        public void finished() {
344            if (trueConstant.hasNoUsages()) {
345                graph.removeFloating(trueConstant);
346            }
347            if (falseConstant.hasNoUsages()) {
348                graph.removeFloating(falseConstant);
349            }
350            super.finished();
351        }
352
353        private void registerCondition(boolean isTrue, LogicNode condition, GuardingNode anchor) {
354            if (!isTrue && condition instanceof ShortCircuitOrNode) {
355                /*
356                 * We can only do this for fixed nodes, because floating guards will be registered
357                 * at a BeginNode but might be "valid" only later due to data flow dependencies.
358                 * Therefore, registering both conditions of a ShortCircuitOrNode for a floating
359                 * guard could lead to cycles in data flow, because the guard will be used as anchor
360                 * for both conditions, and one condition could be depending on the other.
361                 */
362                if (anchor instanceof FixedNode) {
363                    ShortCircuitOrNode disjunction = (ShortCircuitOrNode) condition;
364                    registerCondition(disjunction.isXNegated(), disjunction.getX(), anchor);
365                    registerCondition(disjunction.isYNegated(), disjunction.getY(), anchor);
366                }
367            }
368            state.addCondition(isTrue, condition, anchor);
369
370            if (isTrue && condition instanceof InstanceOfNode) {
371                InstanceOfNode instanceOf = (InstanceOfNode) condition;
372                ValueNode object = instanceOf.getValue();
373                state.addNullness(false, object);
374                state.addType(instanceOf.type(), object);
375            } else if (condition instanceof IsNullNode) {
376                IsNullNode nullCheck = (IsNullNode) condition;
377                state.addNullness(isTrue, nullCheck.getValue());
378            } else if (condition instanceof ObjectEqualsNode) {
379                ObjectEqualsNode equals = (ObjectEqualsNode) condition;
380                ValueNode x = equals.getX();
381                ValueNode y = equals.getY();
382                if (isTrue) {
383                    if (state.isNull(x) && !state.isNull(y)) {
384                        metricObjectEqualsRegistered.increment();
385                        state.addNullness(true, y);
386                    } else if (!state.isNull(x) && state.isNull(y)) {
387                        metricObjectEqualsRegistered.increment();
388                        state.addNullness(true, x);
389                    }
390                    if (state.isNonNull(x) && !state.isNonNull(y)) {
391                        metricObjectEqualsRegistered.increment();
392                        state.addNullness(false, y);
393                    } else if (!state.isNonNull(x) && state.isNonNull(y)) {
394                        metricObjectEqualsRegistered.increment();
395                        state.addNullness(false, x);
396                    }
397                } else {
398                    if (state.isNull(x) && !state.isNonNull(y)) {
399                        metricObjectEqualsRegistered.increment();
400                        state.addNullness(false, y);
401                    } else if (!state.isNonNull(x) && state.isNull(y)) {
402                        metricObjectEqualsRegistered.increment();
403                        state.addNullness(false, x);
404                    }
405                }
406            }
407        }
408
409        private void registerControlSplitInfo(Node pred, AbstractBeginNode begin) {
410            assert pred != null && begin != null;
411            /*
412             * We does not create value proxies for values it may connect accross loop exit node so
413             * we have to clear the state at loop exits if the graph needs value proxies
414             */
415            if (begin instanceof LoopExitNode && begin.graph().hasValueProxies()) {
416                state.clear();
417            }
418
419            if (pred instanceof IfNode) {
420                IfNode ifNode = (IfNode) pred;
421
422                if (!(ifNode.condition() instanceof LogicConstantNode)) {
423                    registerCondition(begin == ifNode.trueSuccessor(), ifNode.condition(), begin);
424                }
425            } else if (pred instanceof TypeSwitchNode) {
426                TypeSwitchNode typeSwitch = (TypeSwitchNode) pred;
427
428                if (typeSwitch.value() instanceof LoadHubNode) {
429                    LoadHubNode loadHub = (LoadHubNode) typeSwitch.value();
430                    ResolvedJavaType type = null;
431                    for (int i = 0; i < typeSwitch.keyCount(); i++) {
432                        if (typeSwitch.keySuccessor(i) == begin) {
433                            if (type == null) {
434                                type = typeSwitch.typeAt(i);
435                            } else {
436                                type = widen(type, typeSwitch.typeAt(i));
437                            }
438                        }
439                    }
440                    if (type != null) {
441                        state.addNullness(false, loadHub.getValue());
442                        state.addType(type, loadHub.getValue());
443                    }
444                }
445            }
446        }
447
448        private GuardedStamp computeGuardedStamp(GuardNode guard) {
449            if (guard.condition() instanceof IntegerBelowNode) {
450                if (guard.isNegated()) {
451                    // Not sure how to reason about negated guards
452                    return null;
453                }
454                IntegerBelowNode below = (IntegerBelowNode) guard.condition();
455                if (below.getX().getKind() == Kind.Int && below.getX().isConstant() && !below.getY().isConstant()) {
456                    Stamp stamp = StampTool.unsignedCompare(below.getX().stamp(), below.getY().stamp());
457                    if (stamp != null) {
458                        return new GuardedStamp(below.getY(), stamp, guard);
459                    }
460                }
461                if (below.getY().getKind() == Kind.Int && below.getY().isConstant() && !below.getX().isConstant()) {
462                    Stamp stamp = StampTool.unsignedCompare(below.getX().stamp(), below.getY().stamp());
463                    if (stamp != null) {
464                        return new GuardedStamp(below.getX(), stamp, guard);
465                    }
466                }
467            }
468            return null;
469        }
470
471        private boolean eliminateTrivialGuardOrRegisterStamp(GuardNode guard) {
472            if (tryReplaceWithExistingGuard(guard)) {
473                return true;
474            }
475            // Can't be eliminated so accumulate any type information from the guard
476            registerConditionalStamp(guard);
477            return false;
478        }
479
480        /**
481         * Replace {@code guard} with {@code anchor} .
482         *
483         * @param guard The guard to eliminate.
484         * @param anchor Node to replace the guard.
485         */
486        private void eliminateGuard(GuardNode guard, GuardingNode anchor) {
487            guard.replaceAtUsages(anchor.asNode());
488            metricGuardsRemoved.increment();
489            GraphUtil.killWithUnusedFloatingInputs(guard);
490        }
491
492        /**
493         * See if a conditional type constraint can prove this guard.
494         *
495         * @param guard
496         * @return true if the guard was eliminated.
497         */
498        private boolean testImpliedGuard(GuardNode guard) {
499            if (state.valueConstraints.size() == 0) {
500                // Nothing to do.
501                return false;
502            }
503
504            GuardNode existingGuard = null;
505            if (guard.condition() instanceof IntegerBelowNode) {
506                IntegerBelowNode below = (IntegerBelowNode) guard.condition();
507                IntegerStamp xStamp = (IntegerStamp) below.getX().stamp();
508                IntegerStamp yStamp = (IntegerStamp) below.getY().stamp();
509                GuardedStamp cstamp = state.valueConstraints.get(below.getX());
510                if (cstamp != null) {
511                    xStamp = (IntegerStamp) cstamp.getStamp();
512                } else {
513                    cstamp = state.valueConstraints.get(below.getY());
514                    if (cstamp != null) {
515                        yStamp = (IntegerStamp) cstamp.getStamp();
516                    }
517                }
518                if (cstamp != null) {
519                    if (cstamp.getGuard() == guard) {
520                        // found ourselves
521                        return false;
522                    }
523                    // See if we can use the other guard
524                    if (!guard.isNegated() && !cstamp.getGuard().isNegated() && yStamp.isPositive()) {
525                        if (xStamp.isPositive() && xStamp.upperBound() < yStamp.lowerBound()) {
526                            // Proven true
527                            existingGuard = cstamp.getGuard();
528                            Debug.log("existing guard %s %1s proves %1s", existingGuard, existingGuard.condition(), guard.condition());
529                        } else if (xStamp.isStrictlyNegative() || xStamp.lowerBound() >= yStamp.upperBound()) {
530                            // An earlier guard proves that this will always fail but it's probably
531                            // not worth trying to use it.
532                        }
533                    }
534                }
535            } else if (guard.condition() instanceof IntegerEqualsNode && guard.isNegated()) {
536                IntegerEqualsNode equals = (IntegerEqualsNode) guard.condition();
537                GuardedStamp cstamp = state.valueConstraints.get(equals.getY());
538                if (cstamp != null && equals.getX().isConstant()) {
539                    IntegerStamp stamp = (IntegerStamp) cstamp.getStamp();
540                    if (!stamp.contains(equals.getX().asJavaConstant().asLong())) {
541                        // x != n is true if n is outside the range of the stamp
542                        existingGuard = cstamp.getGuard();
543                        Debug.log("existing guard %s %1s proves !%1s", existingGuard, existingGuard.condition(), guard.condition());
544                    }
545                }
546            }
547
548            if (existingGuard != null) {
549                // Found a guard which proves this guard to be true, so replace it.
550                eliminateGuard(guard, existingGuard);
551                return true;
552            }
553            return false;
554        }
555
556        private boolean tryReplaceWithExistingGuard(GuardNode guard) {
557            GuardingNode existingGuard = guard.isNegated() ? state.falseConditions.get(guard.condition()) : state.trueConditions.get(guard.condition());
558            if (existingGuard != null && existingGuard != guard) {
559                eliminateGuard(guard, existingGuard);
560                return true;
561            }
562            return false;
563        }
564
565        private void registerConditionalStamp(GuardNode guard) {
566            GuardedStamp conditional = computeGuardedStamp(guard);
567            if (conditional != null) {
568                GuardedStamp other = state.valueConstraints.get(conditional.getValue());
569                if (other == null) {
570                    state.valueConstraints.put(conditional.getValue(), conditional);
571                } else if (guard.isNegated() != other.getGuard().isNegated()) {
572                    // This seems impossible
573                    // Debug.log("negated and !negated guards %1s %1s", guard, other.getGuard());
574                } else if (!guard.isNegated()) {
575                    // two different constraints, pick the one with the tightest type
576                    // information
577                    Stamp result = conditional.getStamp().join(other.getStamp());
578                    if (result == conditional.getStamp()) {
579                        Debug.log("%1s overrides existing value %1s", guard.condition(), other.getGuard().condition());
580                        state.valueConstraints.put(conditional.getValue(), conditional);
581                    } else if (result == other.getStamp()) {
582                        // existing type constraint is best
583                        Debug.log("existing value is best %s", other.getGuard());
584                    } else {
585                        // The merger produced some combination of values
586                        Debug.log("type merge produced new type %s", result);
587                    }
588                }
589            }
590        }
591
592        /**
593         * Determines if, at the current point in the control flow, the condition is known to be
594         * true, false or unknown. In case of true or false the corresponding value is returned,
595         * otherwise null.
596         */
597        private <T extends ValueNode> T evaluateCondition(LogicNode condition, T trueValue, T falseValue) {
598            if (state.trueConditions.containsKey(condition)) {
599                return trueValue;
600            } else if (state.falseConditions.containsKey(condition)) {
601                return falseValue;
602            } else {
603                if (condition instanceof InstanceOfNode) {
604                    InstanceOfNode instanceOf = (InstanceOfNode) condition;
605                    ValueNode object = instanceOf.getValue();
606                    if (state.isNull(object)) {
607                        metricInstanceOfRemoved.increment();
608                        return falseValue;
609                    } else if (state.isNonNull(object)) {
610                        ResolvedJavaType type = state.getNodeType(object);
611                        if (type != null && instanceOf.type().isAssignableFrom(type)) {
612                            metricInstanceOfRemoved.increment();
613                            return trueValue;
614                        }
615                    }
616                } else if (condition instanceof IsNullNode) {
617                    IsNullNode isNull = (IsNullNode) condition;
618                    ValueNode object = isNull.getValue();
619                    if (state.isNull(object)) {
620                        metricNullCheckRemoved.increment();
621                        return trueValue;
622                    } else if (state.isNonNull(object)) {
623                        metricNullCheckRemoved.increment();
624                        return falseValue;
625                    }
626                } else if (condition instanceof ObjectEqualsNode) {
627                    ObjectEqualsNode equals = (ObjectEqualsNode) condition;
628                    ValueNode x = equals.getX();
629                    ValueNode y = equals.getY();
630                    if (state.isNull(x) && state.isNonNull(y) || state.isNonNull(x) && state.isNull(y)) {
631                        metricObjectEqualsRemoved.increment();
632                        return falseValue;
633                    } else if (state.isNull(x) && state.isNull(y)) {
634                        metricObjectEqualsRemoved.increment();
635                        return trueValue;
636                    }
637                }
638            }
639            return null;
640        }
641
642        @Override
643        protected void node(FixedNode node) {
644            if (node instanceof AbstractBeginNode) {
645                processAbstractBegin((AbstractBeginNode) node);
646            } else if (node instanceof FixedGuardNode) {
647                processFixedGuard((FixedGuardNode) node);
648            } else if (node instanceof CheckCastNode) {
649                processCheckCast((CheckCastNode) node);
650            } else if (node instanceof ConditionAnchorNode) {
651                processConditionAnchor((ConditionAnchorNode) node);
652            } else if (node instanceof IfNode) {
653                processIf((IfNode) node);
654            } else if (node instanceof AbstractEndNode) {
655                processAbstractEnd((AbstractEndNode) node);
656            } else if (node instanceof Invoke) {
657                processInvoke((Invoke) node);
658            }
659        }
660
661        private void processIf(IfNode ifNode) {
662            LogicNode compare = ifNode.condition();
663
664            LogicNode replacement = null;
665            GuardingNode replacementAnchor = null;
666            AbstractBeginNode survivingSuccessor = null;
667            if (state.trueConditions.containsKey(compare)) {
668                replacement = trueConstant;
669                replacementAnchor = state.trueConditions.get(compare);
670                survivingSuccessor = ifNode.trueSuccessor();
671            } else if (state.falseConditions.containsKey(compare)) {
672                replacement = falseConstant;
673                replacementAnchor = state.falseConditions.get(compare);
674                survivingSuccessor = ifNode.falseSuccessor();
675            } else {
676                replacement = evaluateCondition(compare, trueConstant, falseConstant);
677                if (replacement != null) {
678                    if (replacement == trueConstant) {
679                        survivingSuccessor = ifNode.trueSuccessor();
680                    } else {
681                        assert replacement == falseConstant;
682                        survivingSuccessor = ifNode.falseSuccessor();
683                    }
684                }
685            }
686
687            if (replacement != null) {
688                trySimplify(ifNode, compare, replacement, replacementAnchor, survivingSuccessor);
689            }
690        }
691
692        private void trySimplify(IfNode ifNode, LogicNode compare, LogicNode replacement, GuardingNode replacementAnchor, AbstractBeginNode survivingSuccessor) {
693            if (replacementAnchor != null && !(replacementAnchor instanceof AbstractBeginNode)) {
694                ValueAnchorNode anchor = graph.add(new ValueAnchorNode(replacementAnchor.asNode()));
695                graph.addBeforeFixed(ifNode, anchor);
696            }
697            boolean canSimplify = true;
698            for (Node n : survivingSuccessor.usages().snapshot()) {
699                if (n instanceof GuardNode || n instanceof ProxyNode) {
700                    // Keep wired to the begin node.
701                } else {
702                    if (replacementAnchor == null) {
703                        // Cannot simplify this IfNode as there is no anchor.
704                        canSimplify = false;
705                        break;
706                    }
707                    // Rewire to the replacement anchor.
708                    n.replaceFirstInput(survivingSuccessor, replacementAnchor.asNode());
709                }
710            }
711
712            if (canSimplify) {
713                ifNode.setCondition(replacement);
714                if (compare.hasNoUsages()) {
715                    GraphUtil.killWithUnusedFloatingInputs(compare);
716                }
717            }
718        }
719
720        private void processInvoke(Invoke invoke) {
721            if (invoke.callTarget() instanceof MethodCallTargetNode) {
722                MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
723                ValueNode receiver = callTarget.receiver();
724                if (receiver != null && callTarget.invokeKind().isIndirect()) {
725                    ResolvedJavaType type = state.getNodeType(receiver);
726                    if (!Objects.equals(type, StampTool.typeOrNull(receiver))) {
727                        ResolvedJavaMethod method = type.resolveConcreteMethod(callTarget.targetMethod(), invoke.getContextType());
728                        if (method != null) {
729                            if (method.canBeStaticallyBound() || type.isLeaf() || type.isArray()) {
730                                callTarget.setInvokeKind(InvokeKind.Special);
731                                callTarget.setTargetMethod(method);
732                            }
733                        }
734                    }
735                }
736            }
737        }
738
739        private void processAbstractEnd(AbstractEndNode endNode) {
740            for (PhiNode phi : endNode.merge().phis()) {
741                int index = endNode.merge().phiPredecessorIndex(endNode);
742                ValueNode value = phi.valueAt(index);
743                if (value instanceof ConditionalNode) {
744                    ConditionalNode materialize = (ConditionalNode) value;
745                    LogicNode compare = materialize.condition();
746                    ValueNode replacement = evaluateCondition(compare, materialize.trueValue(), materialize.falseValue());
747
748                    if (replacement != null) {
749                        phi.setValueAt(index, replacement);
750                        if (materialize.hasNoUsages()) {
751                            GraphUtil.killWithUnusedFloatingInputs(materialize);
752                        }
753                    }
754                }
755            }
756        }
757
758        private void processConditionAnchor(ConditionAnchorNode conditionAnchorNode) {
759            LogicNode condition = conditionAnchorNode.condition();
760            GuardingNode replacementAnchor = null;
761            if (conditionAnchorNode.isNegated()) {
762                if (state.falseConditions.containsKey(condition)) {
763                    replacementAnchor = state.falseConditions.get(condition);
764                }
765            } else {
766                if (state.trueConditions.containsKey(condition)) {
767                    replacementAnchor = state.trueConditions.get(condition);
768                }
769            }
770            if (replacementAnchor != null) {
771                conditionAnchorNode.replaceAtUsages(replacementAnchor.asNode());
772                conditionAnchorNode.graph().removeFixed(conditionAnchorNode);
773            }
774        }
775
776        private void processCheckCast(CheckCastNode checkCast) {
777            ValueNode object = checkCast.object();
778            boolean isNull = state.isNull(object);
779            ResolvedJavaType type = state.getNodeType(object);
780            if (isNull || (type != null && checkCast.type().isAssignableFrom(type))) {
781                boolean nonNull = state.isNonNull(object);
782                // if (true)
783                // throw new RuntimeException(checkCast.toString());
784                GuardingNode replacementAnchor = null;
785                if (nonNull) {
786                    replacementAnchor = searchAnchor(GraphUtil.unproxify(object), type);
787                }
788                if (replacementAnchor == null) {
789                    replacementAnchor = AbstractBeginNode.prevBegin(checkCast);
790                }
791                assert !(replacementAnchor instanceof FloatingNode) : "unsafe to mix unlowered Checkcast with floating guards";
792                PiNode piNode;
793                if (isNull) {
794                    ConstantNode nullObject = ConstantNode.defaultForKind(Kind.Object, graph);
795                    piNode = graph.unique(new PiNode(nullObject, nullObject.stamp(), replacementAnchor.asNode()));
796                } else {
797                    piNode = graph.unique(new PiNode(object, StampFactory.declaredTrusted(type, nonNull), replacementAnchor.asNode()));
798                }
799                checkCast.replaceAtUsages(piNode);
800                graph.removeFixed(checkCast);
801                metricCheckCastRemoved.increment();
802            }
803        }
804
805        private void processFixedGuard(FixedGuardNode guard) {
806            GuardingNode existingGuard = guard.isNegated() ? state.falseConditions.get(guard.condition()) : state.trueConditions.get(guard.condition());
807            if (existingGuard != null && existingGuard instanceof FixedGuardNode) {
808                guard.replaceAtUsages(existingGuard.asNode());
809                guard.graph().removeFixed(guard);
810            } else {
811                registerCondition(!guard.isNegated(), guard.condition(), guard);
812            }
813        }
814
815        private void processAbstractBegin(AbstractBeginNode begin) {
816            Node pred = begin.predecessor();
817
818            if (pred != null) {
819                registerControlSplitInfo(pred, begin);
820            }
821
822            // First eliminate any guards which can be trivially removed and register any
823            // type constraints the guards produce.
824            for (GuardNode guard : begin.guards().snapshot()) {
825                eliminateTrivialGuardOrRegisterStamp(guard);
826            }
827
828            // Collect the guards which have produced conditional stamps.
829            // XXX (gd) IdentityHashMap.values().contains performs a linear search
830            // so we prefer to build a set
831            Set<GuardNode> provers = Node.newSet();
832            for (GuardedStamp e : state.valueConstraints.values()) {
833                provers.add(e.getGuard());
834            }
835
836            // Process the remaining guards. Guards which produced some type constraint should
837            // just be registered since they aren't trivially deleteable. Test the other guards
838            // to see if they can be deleted using type constraints.
839            for (GuardNode guard : begin.guards().snapshot()) {
840                if (provers.contains(guard) || !(tryReplaceWithExistingGuard(guard) || testImpliedGuard(guard))) {
841                    registerCondition(!guard.isNegated(), guard.condition(), guard);
842                }
843            }
844            assert assertImpliedGuard(provers);
845        }
846
847        private boolean assertImpliedGuard(Set<GuardNode> provers) {
848            for (GuardNode guard : provers) {
849                assert !testImpliedGuard(guard) : "provers shouldn't be trivially eliminatable";
850            }
851            return true;
852        }
853
854        private GuardingNode searchAnchor(ValueNode value, ResolvedJavaType type) {
855            for (Node n : value.usages()) {
856                if (n instanceof InstanceOfNode) {
857                    InstanceOfNode instanceOfNode = (InstanceOfNode) n;
858                    if (instanceOfNode.type().equals(type) && state.trueConditions.containsKey(instanceOfNode)) {
859                        GuardingNode v = state.trueConditions.get(instanceOfNode);
860                        if (v != null) {
861                            return v;
862                        }
863                    }
864                }
865            }
866
867            for (Node n : value.usages()) {
868                if (n instanceof ValueProxy) {
869                    ValueProxy proxyNode = (ValueProxy) n;
870                    if (proxyNode.getOriginalNode() == value) {
871                        GuardingNode result = searchAnchor((ValueNode) n, type);
872                        if (result != null) {
873                            return result;
874                        }
875                    }
876
877                }
878            }
879
880            return null;
881        }
882    }
883}