1/*
2 * Copyright (c) 2011, 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 */
23package org.graalvm.compiler.virtual.phases.ea;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
28
29import org.graalvm.compiler.debug.DebugContext;
30import org.graalvm.compiler.graph.Node;
31import org.graalvm.compiler.nodes.FixedNode;
32import org.graalvm.compiler.nodes.FixedWithNextNode;
33import org.graalvm.compiler.nodes.StructuredGraph;
34import org.graalvm.compiler.nodes.ValueNode;
35import org.graalvm.compiler.nodes.calc.FloatingNode;
36import org.graalvm.compiler.nodes.java.MonitorIdNode;
37import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
38import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
39import org.graalvm.compiler.nodes.virtual.LockState;
40import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
41import org.graalvm.compiler.options.OptionValues;
42import org.graalvm.compiler.virtual.phases.ea.EffectList.Effect;
43
44public abstract class PartialEscapeBlockState<T extends PartialEscapeBlockState<T>> extends EffectsBlockState<T> {
45
46    private static final ObjectState[] EMPTY_ARRAY = new ObjectState[0];
47
48    /**
49     * This array contains the state of all virtual objects, indexed by
50     * {@link VirtualObjectNode#getObjectId()}. Entries in this array may be null if the
51     * corresponding virtual object is not alive or reachable currently.
52     */
53    private ObjectState[] objectStates;
54
55    public boolean contains(VirtualObjectNode value) {
56        for (ObjectState state : objectStates) {
57            if (state != null && state.isVirtual() && state.getEntries() != null) {
58                for (ValueNode entry : state.getEntries()) {
59                    if (entry == value) {
60                        return true;
61                    }
62                }
63            }
64        }
65        return false;
66    }
67
68    private static class RefCount {
69        private int refCount = 1;
70    }
71
72    /**
73     * Usage count for the objectStates array, to avoid unneessary copying.
74     */
75    private RefCount arrayRefCount;
76
77    private final OptionValues options;
78    private final DebugContext debug;
79
80    /**
81     * Final subclass of PartialEscapeBlockState, for performance and to make everything behave
82     * nicely with generics.
83     */
84    public static final class Final extends PartialEscapeBlockState<Final> {
85
86        public Final(OptionValues options, DebugContext debug) {
87            super(options, debug);
88        }
89
90        public Final(Final other) {
91            super(other);
92        }
93    }
94
95    protected PartialEscapeBlockState(OptionValues options, DebugContext debug) {
96        objectStates = EMPTY_ARRAY;
97        arrayRefCount = new RefCount();
98        this.options = options;
99        this.debug = debug;
100    }
101
102    protected PartialEscapeBlockState(PartialEscapeBlockState<T> other) {
103        super(other);
104        adoptAddObjectStates(other);
105        options = other.options;
106        debug = other.debug;
107    }
108
109    public ObjectState getObjectState(int object) {
110        ObjectState state = objectStates[object];
111        assert state != null;
112        return state;
113    }
114
115    public ObjectState getObjectStateOptional(int object) {
116        return object >= objectStates.length ? null : objectStates[object];
117    }
118
119    /**
120     * Asserts that the given virtual object is available/reachable in the current state.
121     */
122    public ObjectState getObjectState(VirtualObjectNode object) {
123        ObjectState state = objectStates[object.getObjectId()];
124        assert state != null;
125        return state;
126    }
127
128    public ObjectState getObjectStateOptional(VirtualObjectNode object) {
129        int id = object.getObjectId();
130        return id >= objectStates.length ? null : objectStates[id];
131    }
132
133    private ObjectState[] getObjectStateArrayForModification() {
134        if (arrayRefCount.refCount > 1) {
135            objectStates = objectStates.clone();
136            arrayRefCount.refCount--;
137            arrayRefCount = new RefCount();
138        }
139        return objectStates;
140    }
141
142    private ObjectState getObjectStateForModification(int object) {
143        ObjectState[] array = getObjectStateArrayForModification();
144        ObjectState objectState = array[object];
145        if (objectState.copyOnWrite) {
146            array[object] = objectState = objectState.cloneState();
147        }
148        return objectState;
149    }
150
151    public void setEntry(int object, int entryIndex, ValueNode value) {
152        if (objectStates[object].getEntry(entryIndex) != value) {
153            getObjectStateForModification(object).setEntry(entryIndex, value);
154        }
155    }
156
157    public void escape(int object, ValueNode materialized) {
158        getObjectStateForModification(object).escape(materialized);
159    }
160
161    public void addLock(int object, MonitorIdNode monitorId) {
162        getObjectStateForModification(object).addLock(monitorId);
163    }
164
165    public MonitorIdNode removeLock(int object) {
166        return getObjectStateForModification(object).removeLock();
167    }
168
169    public void setEnsureVirtualized(int object, boolean ensureVirtualized) {
170        if (objectStates[object].getEnsureVirtualized() != ensureVirtualized) {
171            getObjectStateForModification(object).setEnsureVirtualized(ensureVirtualized);
172        }
173    }
174
175    public void updateMaterializedValue(int object, ValueNode value) {
176        if (objectStates[object].getMaterializedValue() != value) {
177            getObjectStateForModification(object).updateMaterializedValue(value);
178        }
179    }
180
181    /**
182     * Materializes the given virtual object and produces the necessary effects in the effects list.
183     * This transitively also materializes all other virtual objects that are reachable from the
184     * entries.
185     */
186    public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
187        PartialEscapeClosure.COUNTER_MATERIALIZATIONS.increment(fixed.getDebug());
188        List<AllocatedObjectNode> objects = new ArrayList<>(2);
189        List<ValueNode> values = new ArrayList<>(8);
190        List<List<MonitorIdNode>> locks = new ArrayList<>();
191        List<ValueNode> otherAllocations = new ArrayList<>(2);
192        List<Boolean> ensureVirtual = new ArrayList<>(2);
193        materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations);
194
195        materializeEffects.addVirtualizationDelta(-(objects.size() + otherAllocations.size()));
196        materializeEffects.add("materializeBefore", new Effect() {
197            @Override
198            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
199                for (ValueNode alloc : otherAllocations) {
200                    ValueNode otherAllocation = graph.addOrUniqueWithInputs(alloc);
201                    if (otherAllocation instanceof FixedWithNextNode) {
202                        graph.addBeforeFixed(fixed, (FixedWithNextNode) otherAllocation);
203                    } else {
204                        assert otherAllocation instanceof FloatingNode;
205                    }
206                }
207                if (!objects.isEmpty()) {
208                    CommitAllocationNode commit;
209                    if (fixed.predecessor() instanceof CommitAllocationNode) {
210                        commit = (CommitAllocationNode) fixed.predecessor();
211                    } else {
212                        commit = graph.add(new CommitAllocationNode());
213                        graph.addBeforeFixed(fixed, commit);
214                    }
215                    for (AllocatedObjectNode obj : objects) {
216                        graph.addWithoutUnique(obj);
217                        commit.getVirtualObjects().add(obj.getVirtualObject());
218                        obj.setCommit(commit);
219                    }
220                    for (ValueNode value : values) {
221                        commit.getValues().add(graph.addOrUniqueWithInputs(value));
222                    }
223                    for (List<MonitorIdNode> monitorIds : locks) {
224                        commit.addLocks(monitorIds);
225                    }
226                    commit.getEnsureVirtual().addAll(ensureVirtual);
227
228                    assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.getUsageCount();
229                    List<AllocatedObjectNode> materializedValues = commit.usages().filter(AllocatedObjectNode.class).snapshot();
230                    for (int i = 0; i < commit.getValues().size(); i++) {
231                        if (materializedValues.contains(commit.getValues().get(i))) {
232                            commit.getValues().set(i, ((AllocatedObjectNode) commit.getValues().get(i)).getVirtualObject());
233                        }
234                    }
235                }
236            }
237        });
238    }
239
240    private void materializeWithCommit(FixedNode fixed, VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<List<MonitorIdNode>> locks, List<ValueNode> values,
241                    List<Boolean> ensureVirtual, List<ValueNode> otherAllocations) {
242        ObjectState obj = getObjectState(virtual);
243
244        ValueNode[] entries = obj.getEntries();
245        ValueNode representation = virtual.getMaterializedRepresentation(fixed, entries, obj.getLocks());
246        escape(virtual.getObjectId(), representation);
247        obj = getObjectState(virtual);
248        PartialEscapeClosure.updateStatesForMaterialized(this, virtual, obj.getMaterializedValue());
249        if (representation instanceof AllocatedObjectNode) {
250            objects.add((AllocatedObjectNode) representation);
251            locks.add(LockState.asList(obj.getLocks()));
252            ensureVirtual.add(obj.getEnsureVirtualized());
253            int pos = values.size();
254            while (values.size() < pos + entries.length) {
255                values.add(null);
256            }
257            for (int i = 0; i < entries.length; i++) {
258                if (entries[i] instanceof VirtualObjectNode) {
259                    VirtualObjectNode entryVirtual = (VirtualObjectNode) entries[i];
260                    ObjectState entryObj = getObjectState(entryVirtual);
261                    if (entryObj.isVirtual()) {
262                        materializeWithCommit(fixed, entryVirtual, objects, locks, values, ensureVirtual, otherAllocations);
263                        entryObj = getObjectState(entryVirtual);
264                    }
265                    values.set(pos + i, entryObj.getMaterializedValue());
266                } else {
267                    values.set(pos + i, entries[i]);
268                }
269            }
270            objectMaterialized(virtual, (AllocatedObjectNode) representation, values.subList(pos, pos + entries.length));
271        } else {
272            VirtualUtil.trace(options, debug, "materialized %s as %s", virtual, representation);
273            otherAllocations.add(representation);
274            assert obj.getLocks() == null;
275        }
276    }
277
278    protected void objectMaterialized(VirtualObjectNode virtual, AllocatedObjectNode representation, List<ValueNode> values) {
279        VirtualUtil.trace(options, debug, "materialized %s as %s with values %s", virtual, representation, values);
280    }
281
282    public void addObject(int virtual, ObjectState state) {
283        ensureSize(virtual)[virtual] = state;
284    }
285
286    private ObjectState[] ensureSize(int objectId) {
287        if (objectStates.length <= objectId) {
288            objectStates = Arrays.copyOf(objectStates, Math.max(objectId * 2, 4));
289            arrayRefCount.refCount--;
290            arrayRefCount = new RefCount();
291            return objectStates;
292        } else {
293            return getObjectStateArrayForModification();
294        }
295    }
296
297    public int getStateCount() {
298        return objectStates.length;
299    }
300
301    @Override
302    public String toString() {
303        return super.toString() + ", Object States: " + Arrays.toString(objectStates);
304    }
305
306    @Override
307    public boolean equivalentTo(T other) {
308        int length = Math.max(objectStates.length, other.getStateCount());
309        for (int i = 0; i < length; i++) {
310            ObjectState left = getObjectStateOptional(i);
311            ObjectState right = other.getObjectStateOptional(i);
312            if (left != right) {
313                if (left == null || right == null) {
314                    return false;
315                }
316                if (!left.equals(right)) {
317                    return false;
318                }
319            }
320        }
321        return true;
322    }
323
324    public void resetObjectStates(int size) {
325        objectStates = new ObjectState[size];
326    }
327
328    public static boolean identicalObjectStates(PartialEscapeBlockState<?>[] states) {
329        for (int i = 1; i < states.length; i++) {
330            if (states[0].objectStates != states[i].objectStates) {
331                return false;
332            }
333        }
334        return true;
335    }
336
337    public static boolean identicalObjectStates(PartialEscapeBlockState<?>[] states, int object) {
338        for (int i = 1; i < states.length; i++) {
339            if (states[0].objectStates[object] != states[i].objectStates[object]) {
340                return false;
341            }
342        }
343        return true;
344    }
345
346    public void adoptAddObjectStates(PartialEscapeBlockState<?> other) {
347        if (objectStates != null) {
348            arrayRefCount.refCount--;
349        }
350        objectStates = other.objectStates;
351        arrayRefCount = other.arrayRefCount;
352
353        if (arrayRefCount.refCount == 1) {
354            for (ObjectState state : objectStates) {
355                if (state != null) {
356                    state.share();
357                }
358            }
359        }
360        arrayRefCount.refCount++;
361    }
362}
363