1 /*
2  * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.virtual.phases.ea;
26 
27 import java.lang.reflect.Field;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Iterator;
31 
32 import org.graalvm.compiler.debug.DebugContext;
33 import org.graalvm.compiler.debug.GraalError;
34 import org.graalvm.compiler.graph.Node;
35 import org.graalvm.compiler.nodes.StructuredGraph;
36 
37 /**
38  * An {@link EffectList} can be used to maintain a list of {@link Effect}s and backtrack to a
39  * previous state by truncating the list.
40  */
41 public class EffectList implements Iterable<EffectList.Effect> {
42 
43     public interface Effect {
isVisible()44         default boolean isVisible() {
45             return true;
46         }
47 
isCfgKill()48         default boolean isCfgKill() {
49             return false;
50         }
51 
apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes)52         void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes);
53     }
54 
55     public interface SimpleEffect extends Effect {
56         @Override
apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes)57         default void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
58             apply(graph);
59         }
60 
apply(StructuredGraph graph)61         void apply(StructuredGraph graph);
62     }
63 
64     private static final Effect[] EMPTY_ARRAY = new Effect[0];
65     private static final String[] EMPTY_STRING_ARRAY = new String[0];
66 
67     private final DebugContext debug;
68     private Effect[] effects = EMPTY_ARRAY;
69     private String[] names = EMPTY_STRING_ARRAY;
70     private int size;
71 
EffectList(DebugContext debug)72     public EffectList(DebugContext debug) {
73         this.debug = debug;
74     }
75 
enlarge(int elements)76     private void enlarge(int elements) {
77         int length = effects.length;
78         if (size + elements > length) {
79             while (size + elements > length) {
80                 length = Math.max(length * 2, 4);
81             }
82             effects = Arrays.copyOf(effects, length);
83             if (debug.isLogEnabled()) {
84                 names = Arrays.copyOf(names, length);
85             }
86         }
87     }
88 
add(String name, SimpleEffect effect)89     public void add(String name, SimpleEffect effect) {
90         add(name, (Effect) effect);
91     }
92 
add(String name, Effect effect)93     public void add(String name, Effect effect) {
94         assert effect != null;
95         enlarge(1);
96         if (debug.isLogEnabled()) {
97             names[size] = name;
98         }
99         effects[size++] = effect;
100     }
101 
addAll(EffectList list)102     public void addAll(EffectList list) {
103         enlarge(list.size);
104         System.arraycopy(list.effects, 0, effects, size, list.size);
105         if (debug.isLogEnabled()) {
106             System.arraycopy(list.names, 0, names, size, list.size);
107         }
108         size += list.size;
109     }
110 
insertAll(EffectList list, int position)111     public void insertAll(EffectList list, int position) {
112         assert position >= 0 && position <= size;
113         enlarge(list.size);
114         System.arraycopy(effects, position, effects, position + list.size, size - position);
115         System.arraycopy(list.effects, 0, effects, position, list.size);
116         if (debug.isLogEnabled()) {
117             System.arraycopy(names, position, names, position + list.size, size - position);
118             System.arraycopy(list.names, 0, names, position, list.size);
119         }
120         size += list.size;
121     }
122 
checkpoint()123     public int checkpoint() {
124         return size;
125     }
126 
size()127     public int size() {
128         return size;
129     }
130 
backtrack(int checkpoint)131     public void backtrack(int checkpoint) {
132         assert checkpoint <= size;
133         size = checkpoint;
134     }
135 
136     @Override
iterator()137     public Iterator<Effect> iterator() {
138         return new Iterator<Effect>() {
139 
140             int index;
141             final int listSize = EffectList.this.size;
142 
143             @Override
144             public boolean hasNext() {
145                 return index < listSize;
146             }
147 
148             @Override
149             public Effect next() {
150                 return effects[index++];
151             }
152 
153             @Override
154             public void remove() {
155                 throw new UnsupportedOperationException();
156             }
157         };
158     }
159 
get(int index)160     public Effect get(int index) {
161         if (index >= size) {
162             throw new IndexOutOfBoundsException();
163         }
164         return effects[index];
165     }
166 
clear()167     public void clear() {
168         size = 0;
169     }
170 
isEmpty()171     public boolean isEmpty() {
172         return size == 0;
173     }
174 
apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes, boolean cfgKills)175     public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes, boolean cfgKills) {
176         boolean message = false;
177         for (int i = 0; i < size(); i++) {
178             Effect effect = effects[i];
179             if (effect.isCfgKill() == cfgKills) {
180                 if (!message) {
181                     message = true;
182                     debug.log(cfgKills ? " ==== cfg kill effects" : " ==== effects");
183                 }
184                 try {
185                     effect.apply(graph, obsoleteNodes);
186                 } catch (Throwable t) {
187                     StringBuilder str = new StringBuilder();
188                     toString(str, i);
189                     throw new GraalError(t).addContext("effect", str);
190                 }
191                 if (effect.isVisible() && debug.isLogEnabled()) {
192                     StringBuilder str = new StringBuilder();
193                     toString(str, i);
194                     debug.log("    %s", str);
195                 }
196             }
197         }
198     }
199 
toString(StringBuilder str, int i)200     private void toString(StringBuilder str, int i) {
201         Effect effect = effects[i];
202         str.append(getName(i)).append(" [");
203         boolean first = true;
204         for (Field field : effect.getClass().getDeclaredFields()) {
205             try {
206                 field.setAccessible(true);
207                 Object object = field.get(effect);
208                 if (object == this) {
209                     // Inner classes could capture the EffectList itself.
210                     continue;
211                 }
212                 str.append(first ? "" : ", ").append(field.getName()).append("=").append(format(object));
213                 first = false;
214             } catch (SecurityException | IllegalAccessException e) {
215                 throw new RuntimeException(e);
216             }
217         }
218         str.append(']');
219     }
220 
format(Object object)221     private static String format(Object object) {
222         if (object != null && Object[].class.isAssignableFrom(object.getClass())) {
223             return Arrays.toString((Object[]) object);
224         }
225         return "" + object;
226     }
227 
228     @Override
toString()229     public String toString() {
230         StringBuilder str = new StringBuilder();
231         for (int i = 0; i < size(); i++) {
232             Effect effect = get(i);
233             if (effect.isVisible()) {
234                 toString(str, i);
235                 str.append('\n');
236             }
237         }
238         return str.toString();
239     }
240 
getName(int i)241     private String getName(int i) {
242         if (debug.isLogEnabled()) {
243             return names[i];
244         } else {
245             return "";
246         }
247     }
248 }
249