001/*
002 * Copyright (c) 2012, 2015, 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.compiler.common.*;
030import com.oracle.graal.graph.Graph.DuplicationReplacement;
031import com.oracle.graal.graph.*;
032import com.oracle.graal.graph.iterators.*;
033import com.oracle.graal.nodes.*;
034import com.oracle.graal.nodes.VirtualState.NodeClosure;
035import com.oracle.graal.nodes.memory.*;
036import com.oracle.graal.nodes.util.*;
037
038public class LoopFragmentInside extends LoopFragment {
039
040    /**
041     * mergedInitializers. When an inside fragment's (loop)ends are merged to create a unique exit
042     * point, some phis must be created : they phis together all the back-values of the loop-phis
043     * These can then be used to update the loop-phis' forward edge value ('initializer') in the
044     * peeling case. In the unrolling case they will be used as the value that replace the loop-phis
045     * of the duplicated inside fragment
046     */
047    private Map<ValuePhiNode, ValueNode> mergedInitializers;
048    private final DuplicationReplacement dataFixBefore = new DuplicationReplacement() {
049
050        @Override
051        public Node replacement(Node oriInput) {
052            if (!(oriInput instanceof ValueNode)) {
053                return oriInput;
054            }
055            return prim((ValueNode) oriInput);
056        }
057    };
058
059    public LoopFragmentInside(LoopEx loop) {
060        super(loop);
061    }
062
063    public LoopFragmentInside(LoopFragmentInside original) {
064        super(null, original);
065    }
066
067    @Override
068    public LoopFragmentInside duplicate() {
069        assert !isDuplicate();
070        return new LoopFragmentInside(this);
071    }
072
073    @Override
074    public LoopFragmentInside original() {
075        return (LoopFragmentInside) super.original();
076    }
077
078    @SuppressWarnings("unused")
079    public void appendInside(LoopEx loop) {
080        // TODO (gd)
081    }
082
083    @Override
084    public LoopEx loop() {
085        assert !this.isDuplicate();
086        return super.loop();
087    }
088
089    @Override
090    public void insertBefore(LoopEx loop) {
091        assert this.isDuplicate() && this.original().loop() == loop;
092
093        patchNodes(dataFixBefore);
094
095        AbstractBeginNode end = mergeEnds();
096
097        mergeEarlyExits();
098
099        original().patchPeeling(this);
100
101        AbstractBeginNode entry = getDuplicatedNode(loop.loopBegin());
102        loop.entryPoint().replaceAtPredecessor(entry);
103        end.setNext(loop.entryPoint());
104    }
105
106    @Override
107    public NodeBitMap nodes() {
108        if (nodes == null) {
109            LoopFragmentWhole whole = loop().whole();
110            whole.nodes(); // init nodes bitmap in whole
111            nodes = whole.nodes.copy();
112            // remove the phis
113            LoopBeginNode loopBegin = loop().loopBegin();
114            for (PhiNode phi : loopBegin.phis()) {
115                nodes.clear(phi);
116            }
117            clearStateNodes(loopBegin);
118            for (LoopExitNode exit : exits()) {
119                clearStateNodes(exit);
120                for (ProxyNode proxy : exit.proxies()) {
121                    nodes.clear(proxy);
122                }
123            }
124        }
125        return nodes;
126    }
127
128    private void clearStateNodes(StateSplit stateSplit) {
129        FrameState loopState = stateSplit.stateAfter();
130        if (loopState != null) {
131            loopState.applyToVirtual(v -> {
132                if (v.usages().filter(n -> nodes.isMarked(n) && n != stateSplit).isEmpty()) {
133                    nodes.clear(v);
134                }
135            });
136        }
137    }
138
139    public NodeIterable<LoopExitNode> exits() {
140        return loop().loopBegin().loopExits();
141    }
142
143    @Override
144    protected DuplicationReplacement getDuplicationReplacement() {
145        final LoopBeginNode loopBegin = loop().loopBegin();
146        final StructuredGraph graph = graph();
147        return new DuplicationReplacement() {
148
149            private Map<Node, Node> seenNode = Node.newMap();
150
151            @Override
152            public Node replacement(Node original) {
153                if (original == loopBegin) {
154                    Node value = seenNode.get(original);
155                    if (value != null) {
156                        return value;
157                    }
158                    AbstractBeginNode newValue = graph.add(new BeginNode());
159                    seenNode.put(original, newValue);
160                    return newValue;
161                }
162                if (original instanceof LoopExitNode && ((LoopExitNode) original).loopBegin() == loopBegin) {
163                    Node value = seenNode.get(original);
164                    if (value != null) {
165                        return value;
166                    }
167                    AbstractBeginNode newValue = graph.add(new BeginNode());
168                    seenNode.put(original, newValue);
169                    return newValue;
170                }
171                if (original instanceof LoopEndNode && ((LoopEndNode) original).loopBegin() == loopBegin) {
172                    Node value = seenNode.get(original);
173                    if (value != null) {
174                        return value;
175                    }
176                    EndNode newValue = graph.add(new EndNode());
177                    seenNode.put(original, newValue);
178                    return newValue;
179                }
180                return original;
181            }
182        };
183    }
184
185    @Override
186    protected void finishDuplication() {
187        // TODO (gd) ?
188    }
189
190    private static PhiNode patchPhi(StructuredGraph graph, PhiNode phi, AbstractMergeNode merge) {
191        PhiNode ret;
192        if (phi instanceof ValuePhiNode) {
193            ret = new ValuePhiNode(phi.stamp(), merge);
194        } else if (phi instanceof GuardPhiNode) {
195            ret = new GuardPhiNode(merge);
196        } else if (phi instanceof MemoryPhiNode) {
197            ret = new MemoryPhiNode(merge, ((MemoryPhiNode) phi).getLocationIdentity());
198        } else {
199            throw JVMCIError.shouldNotReachHere();
200        }
201        return graph.addWithoutUnique(ret);
202    }
203
204    private void patchPeeling(LoopFragmentInside peel) {
205        LoopBeginNode loopBegin = loop().loopBegin();
206        StructuredGraph graph = loopBegin.graph();
207        List<PhiNode> newPhis = new LinkedList<>();
208
209        NodeBitMap usagesToPatch = nodes.copy();
210        for (LoopExitNode exit : exits()) {
211            markStateNodes(exit, usagesToPatch);
212            for (ProxyNode proxy : exit.proxies()) {
213                usagesToPatch.markAndGrow(proxy);
214            }
215        }
216        markStateNodes(loopBegin, usagesToPatch);
217
218        for (PhiNode phi : loopBegin.phis().snapshot()) {
219            if (phi.hasNoUsages()) {
220                continue;
221            }
222            ValueNode first;
223            if (loopBegin.loopEnds().count() == 1) {
224                ValueNode b = phi.valueAt(loopBegin.loopEnds().first()); // back edge value
225                first = peel.prim(b); // corresponding value in the peel
226            } else {
227                first = peel.mergedInitializers.get(phi);
228            }
229            // create a new phi (we don't patch the old one since some usages of the old one may
230            // still be valid)
231            PhiNode newPhi = patchPhi(graph, phi, loopBegin);
232            newPhi.addInput(first);
233            for (LoopEndNode end : loopBegin.orderedLoopEnds()) {
234                newPhi.addInput(phi.valueAt(end));
235            }
236            peel.putDuplicatedNode(phi, newPhi);
237            newPhis.add(newPhi);
238            for (Node usage : phi.usages().snapshot()) {
239                // patch only usages that should use the new phi ie usages that were peeled
240                if (usagesToPatch.isMarkedAndGrow(usage)) {
241                    usage.replaceFirstInput(phi, newPhi);
242                }
243            }
244        }
245        // check new phis to see if they have as input some old phis, replace those inputs with the
246        // new corresponding phis
247        for (PhiNode phi : newPhis) {
248            for (int i = 0; i < phi.valueCount(); i++) {
249                ValueNode v = phi.valueAt(i);
250                if (loopBegin.isPhiAtMerge(v)) {
251                    PhiNode newV = peel.getDuplicatedNode((ValuePhiNode) v);
252                    if (newV != null) {
253                        phi.setValueAt(i, newV);
254                    }
255                }
256            }
257        }
258
259        for (PhiNode deadPhi : loopBegin.phis().filter(n -> n.hasNoUsages()).snapshot()) {
260            if (deadPhi.isAlive()) {
261                GraphUtil.killWithUnusedFloatingInputs(deadPhi);
262            }
263        }
264    }
265
266    private static void markStateNodes(StateSplit stateSplit, NodeBitMap marks) {
267        FrameState exitState = stateSplit.stateAfter();
268        if (exitState != null) {
269            exitState.applyToVirtual(v -> marks.markAndGrow(v));
270        }
271    }
272
273    /**
274     * Gets the corresponding value in this fragment.
275     *
276     * @param b original value
277     * @return corresponding value in the peel
278     */
279    @Override
280    protected ValueNode prim(ValueNode b) {
281        assert isDuplicate();
282        LoopBeginNode loopBegin = original().loop().loopBegin();
283        if (loopBegin.isPhiAtMerge(b)) {
284            PhiNode phi = (PhiNode) b;
285            return phi.valueAt(loopBegin.forwardEnd());
286        } else if (nodesReady) {
287            ValueNode v = getDuplicatedNode(b);
288            if (v == null) {
289                return b;
290            }
291            return v;
292        } else {
293            return b;
294        }
295    }
296
297    private AbstractBeginNode mergeEnds() {
298        assert isDuplicate();
299        List<EndNode> endsToMerge = new LinkedList<>();
300        // map peel exits to the corresponding loop exits
301        Map<AbstractEndNode, LoopEndNode> reverseEnds = CollectionsFactory.newMap();
302        LoopBeginNode loopBegin = original().loop().loopBegin();
303        for (LoopEndNode le : loopBegin.loopEnds()) {
304            AbstractEndNode duplicate = getDuplicatedNode(le);
305            if (duplicate != null) {
306                endsToMerge.add((EndNode) duplicate);
307                reverseEnds.put(duplicate, le);
308            }
309        }
310        mergedInitializers = Node.newIdentityMap();
311        AbstractBeginNode newExit;
312        StructuredGraph graph = graph();
313        if (endsToMerge.size() == 1) {
314            AbstractEndNode end = endsToMerge.get(0);
315            assert end.hasNoUsages();
316            newExit = graph.add(new BeginNode());
317            end.replaceAtPredecessor(newExit);
318            end.safeDelete();
319        } else {
320            assert endsToMerge.size() > 1;
321            AbstractMergeNode newExitMerge = graph.add(new MergeNode());
322            newExit = newExitMerge;
323            FrameState state = loopBegin.stateAfter();
324            FrameState duplicateState = null;
325            if (state != null) {
326                duplicateState = state.duplicateWithVirtualState();
327                newExitMerge.setStateAfter(duplicateState);
328            }
329            for (EndNode end : endsToMerge) {
330                newExitMerge.addForwardEnd(end);
331            }
332
333            for (final PhiNode phi : loopBegin.phis().snapshot()) {
334                if (phi.hasNoUsages()) {
335                    continue;
336                }
337                final PhiNode firstPhi = patchPhi(graph, phi, newExitMerge);
338                for (AbstractEndNode end : newExitMerge.forwardEnds()) {
339                    LoopEndNode loopEnd = reverseEnds.get(end);
340                    ValueNode prim = prim(phi.valueAt(loopEnd));
341                    assert prim != null;
342                    firstPhi.addInput(prim);
343                }
344                ValueNode initializer = firstPhi;
345                if (duplicateState != null) {
346                    // fix the merge's state after
347                    duplicateState.applyToNonVirtual(new NodeClosure<ValueNode>() {
348
349                        @Override
350                        public void apply(Node from, ValueNode node) {
351                            if (node == phi) {
352                                from.replaceFirstInput(phi, firstPhi);
353                            }
354                        }
355                    });
356                }
357                mergedInitializers.put((ValuePhiNode) phi, initializer);
358            }
359        }
360        return newExit;
361    }
362}