001/*
002 * Copyright (c) 2012, 2012, 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.loop;
024
025import java.util.*;
026
027import jdk.internal.jvmci.common.*;
028
029import com.oracle.graal.graph.*;
030import com.oracle.graal.graph.Graph.DuplicationReplacement;
031import com.oracle.graal.graph.iterators.*;
032import com.oracle.graal.nodes.*;
033import com.oracle.graal.nodes.cfg.*;
034import com.oracle.graal.nodes.java.*;
035import com.oracle.graal.nodes.spi.*;
036import com.oracle.graal.nodes.virtual.*;
037
038public abstract class LoopFragment {
039
040    private final LoopEx loop;
041    private final LoopFragment original;
042    protected NodeBitMap nodes;
043    protected boolean nodesReady;
044    private Map<Node, Node> duplicationMap;
045
046    public LoopFragment(LoopEx loop) {
047        this(loop, null);
048        this.nodesReady = true;
049    }
050
051    public LoopFragment(LoopEx loop, LoopFragment original) {
052        this.loop = loop;
053        this.original = original;
054        this.nodesReady = false;
055    }
056
057    public LoopEx loop() {
058        return loop;
059    }
060
061    public abstract LoopFragment duplicate();
062
063    public abstract void insertBefore(LoopEx l);
064
065    public void disconnect() {
066        // TODO (gd) possibly abstract
067    }
068
069    public boolean contains(Node n) {
070        return nodes().isMarkedAndGrow(n);
071    }
072
073    @SuppressWarnings("unchecked")
074    public <New extends Node, Old extends New> New getDuplicatedNode(Old n) {
075        assert isDuplicate();
076        return (New) duplicationMap.get(n);
077    }
078
079    protected <New extends Node, Old extends New> void putDuplicatedNode(Old oldNode, New newNode) {
080        duplicationMap.put(oldNode, newNode);
081    }
082
083    /**
084     * Gets the corresponding value in this fragment. Should be called on duplicate fragments with a
085     * node from the original fragment as argument.
086     *
087     * @param b original value
088     * @return corresponding value in the peel
089     */
090    protected abstract ValueNode prim(ValueNode b);
091
092    public boolean isDuplicate() {
093        return original != null;
094    }
095
096    public LoopFragment original() {
097        return original;
098    }
099
100    public abstract NodeBitMap nodes();
101
102    public StructuredGraph graph() {
103        LoopEx l;
104        if (isDuplicate()) {
105            l = original().loop();
106        } else {
107            l = loop();
108        }
109        return l.loopBegin().graph();
110    }
111
112    protected abstract DuplicationReplacement getDuplicationReplacement();
113
114    protected abstract void finishDuplication();
115
116    protected void patchNodes(final DuplicationReplacement dataFix) {
117        if (isDuplicate() && !nodesReady) {
118            assert !original.isDuplicate();
119            final DuplicationReplacement cfgFix = original().getDuplicationReplacement();
120            DuplicationReplacement dr;
121            if (cfgFix == null && dataFix != null) {
122                dr = dataFix;
123            } else if (cfgFix != null && dataFix == null) {
124                dr = cfgFix;
125            } else if (cfgFix != null && dataFix != null) {
126                dr = new DuplicationReplacement() {
127
128                    @Override
129                    public Node replacement(Node o) {
130                        Node r1 = dataFix.replacement(o);
131                        if (r1 != o) {
132                            assert cfgFix.replacement(o) == o;
133                            return r1;
134                        }
135                        Node r2 = cfgFix.replacement(o);
136                        if (r2 != o) {
137                            return r2;
138                        }
139                        return o;
140                    }
141                };
142            } else {
143                dr = null;
144            }
145            NodeIterable<Node> nodesIterable = original().nodes();
146            duplicationMap = graph().addDuplicates(nodesIterable, graph(), nodesIterable.count(), dr);
147            finishDuplication();
148            nodesReady = true;
149        } else {
150            // TODO (gd) apply fix ?
151        }
152    }
153
154    protected static NodeBitMap computeNodes(Graph graph, Iterable<AbstractBeginNode> blocks) {
155        return computeNodes(graph, blocks, Collections.emptyList());
156    }
157
158    protected static NodeBitMap computeNodes(Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<LoopExitNode> earlyExits) {
159        final NodeBitMap nodes = graph.createNodeBitMap();
160        for (AbstractBeginNode b : blocks) {
161            if (b.isDeleted()) {
162                continue;
163            }
164
165            for (Node n : b.getBlockNodes()) {
166                if (n instanceof Invoke) {
167                    nodes.mark(((Invoke) n).callTarget());
168                }
169                if (n instanceof NodeWithState) {
170                    NodeWithState withState = (NodeWithState) n;
171                    withState.states().forEach(state -> state.applyToVirtual(node -> nodes.mark(node)));
172                }
173                nodes.mark(n);
174            }
175        }
176        for (LoopExitNode earlyExit : earlyExits) {
177            if (earlyExit.isDeleted()) {
178                continue;
179            }
180
181            FrameState stateAfter = earlyExit.stateAfter();
182            if (stateAfter != null) {
183                stateAfter.applyToVirtual(node -> nodes.mark(node));
184            }
185            nodes.mark(earlyExit);
186            for (ProxyNode proxy : earlyExit.proxies()) {
187                nodes.mark(proxy);
188            }
189        }
190
191        final NodeBitMap notloopNodes = graph.createNodeBitMap();
192        for (AbstractBeginNode b : blocks) {
193            if (b.isDeleted()) {
194                continue;
195            }
196
197            for (Node n : b.getBlockNodes()) {
198                if (n instanceof CommitAllocationNode) {
199                    for (VirtualObjectNode obj : ((CommitAllocationNode) n).getVirtualObjects()) {
200                        markFloating(obj, nodes, notloopNodes);
201                    }
202                }
203                if (n instanceof MonitorEnterNode) {
204                    markFloating(((MonitorEnterNode) n).getMonitorId(), nodes, notloopNodes);
205                }
206                for (Node usage : n.usages()) {
207                    markFloating(usage, nodes, notloopNodes);
208                }
209            }
210        }
211
212        return nodes;
213    }
214
215    private static boolean markFloating(Node n, NodeBitMap loopNodes, NodeBitMap notloopNodes) {
216        if (loopNodes.isMarked(n)) {
217            return true;
218        }
219        if (notloopNodes.isMarked(n)) {
220            return false;
221        }
222        if (n instanceof FixedNode) {
223            return false;
224        }
225        boolean mark = false;
226        if (n instanceof PhiNode) {
227            PhiNode phi = (PhiNode) n;
228            mark = loopNodes.isMarked(phi.merge());
229            if (mark) {
230                loopNodes.mark(n);
231            } else {
232                notloopNodes.mark(n);
233                return false;
234            }
235        }
236        for (Node usage : n.usages()) {
237            if (markFloating(usage, loopNodes, notloopNodes)) {
238                mark = true;
239            }
240        }
241        if (mark) {
242            loopNodes.mark(n);
243            return true;
244        }
245        notloopNodes.mark(n);
246        return false;
247    }
248
249    public static NodeIterable<AbstractBeginNode> toHirBlocks(final Iterable<Block> blocks) {
250        return new NodeIterable<AbstractBeginNode>() {
251
252            public Iterator<AbstractBeginNode> iterator() {
253                final Iterator<Block> it = blocks.iterator();
254                return new Iterator<AbstractBeginNode>() {
255
256                    @Override
257                    public void remove() {
258                        throw new UnsupportedOperationException();
259                    }
260
261                    public AbstractBeginNode next() {
262                        return it.next().getBeginNode();
263                    }
264
265                    public boolean hasNext() {
266                        return it.hasNext();
267                    }
268                };
269            }
270
271        };
272    }
273
274    public static NodeIterable<LoopExitNode> toHirExits(final Iterable<Block> blocks) {
275        return new NodeIterable<LoopExitNode>() {
276
277            public Iterator<LoopExitNode> iterator() {
278                final Iterator<Block> it = blocks.iterator();
279                return new Iterator<LoopExitNode>() {
280
281                    @Override
282                    public void remove() {
283                        throw new UnsupportedOperationException();
284                    }
285
286                    public LoopExitNode next() {
287                        return (LoopExitNode) it.next().getBeginNode();
288                    }
289
290                    public boolean hasNext() {
291                        return it.hasNext();
292                    }
293                };
294            }
295
296        };
297    }
298
299    /**
300     * Merges the early exits (i.e. loop exits) that were duplicated as part of this fragment, with
301     * the original fragment's exits.
302     */
303    protected void mergeEarlyExits() {
304        assert isDuplicate();
305        StructuredGraph graph = graph();
306        for (AbstractBeginNode earlyExit : LoopFragment.toHirBlocks(original().loop().loop().getExits())) {
307            LoopExitNode loopEarlyExit = (LoopExitNode) earlyExit;
308            FixedNode next = loopEarlyExit.next();
309            if (loopEarlyExit.isDeleted() || !this.original().contains(loopEarlyExit)) {
310                continue;
311            }
312            AbstractBeginNode newEarlyExit = getDuplicatedNode(loopEarlyExit);
313            if (newEarlyExit == null) {
314                continue;
315            }
316            MergeNode merge = graph.add(new MergeNode());
317            EndNode originalEnd = graph.add(new EndNode());
318            EndNode newEnd = graph.add(new EndNode());
319            merge.addForwardEnd(originalEnd);
320            merge.addForwardEnd(newEnd);
321            loopEarlyExit.setNext(originalEnd);
322            newEarlyExit.setNext(newEnd);
323            merge.setNext(next);
324
325            FrameState exitState = loopEarlyExit.stateAfter();
326            if (exitState != null) {
327                FrameState originalExitState = exitState;
328                exitState = exitState.duplicateWithVirtualState();
329                loopEarlyExit.setStateAfter(exitState);
330                merge.setStateAfter(originalExitState);
331                /*
332                 * Using the old exit's state as the merge's state is necessary because some of the
333                 * VirtualState nodes contained in the old exit's state may be shared by other
334                 * dominated VirtualStates. Those dominated virtual states need to see the
335                 * proxy->phi update that are applied below.
336                 *
337                 * We now update the original fragment's nodes accordingly:
338                 */
339                originalExitState.applyToVirtual(node -> original.nodes.clearAndGrow(node));
340                exitState.applyToVirtual(node -> original.nodes.markAndGrow(node));
341            }
342            FrameState finalExitState = exitState;
343
344            for (Node anchored : loopEarlyExit.anchored().snapshot()) {
345                anchored.replaceFirstInput(loopEarlyExit, merge);
346            }
347
348            boolean newEarlyExitIsLoopExit = newEarlyExit instanceof LoopExitNode;
349            for (ProxyNode vpn : loopEarlyExit.proxies().snapshot()) {
350                if (vpn.hasNoUsages()) {
351                    continue;
352                }
353                if (vpn.value() == null) {
354                    assert vpn instanceof GuardProxyNode;
355                    vpn.replaceAtUsages(null);
356                    continue;
357                }
358                final ValueNode replaceWith;
359                ValueNode newVpn = prim(newEarlyExitIsLoopExit ? vpn : vpn.value());
360                if (newVpn != null) {
361                    PhiNode phi;
362                    if (vpn instanceof ValueProxyNode) {
363                        phi = graph.addWithoutUnique(new ValuePhiNode(vpn.stamp(), merge));
364                    } else if (vpn instanceof GuardProxyNode) {
365                        phi = graph.addWithoutUnique(new GuardPhiNode(merge));
366                    } else {
367                        throw JVMCIError.shouldNotReachHere();
368                    }
369                    phi.addInput(vpn);
370                    phi.addInput(newVpn);
371                    replaceWith = phi;
372                } else {
373                    replaceWith = vpn.value();
374                }
375                vpn.replaceAtMatchingUsages(replaceWith, usage -> {
376                    if (merge.isPhiAtMerge(usage)) {
377                        return false;
378                    }
379                    if (usage instanceof VirtualState) {
380                        VirtualState stateUsage = (VirtualState) usage;
381                        if (finalExitState.isPartOfThisState(stateUsage)) {
382                            return false;
383                        }
384                    }
385                    return true;
386                });
387            }
388        }
389    }
390}