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.Arrays;
26import java.util.List;
27
28import org.graalvm.compiler.debug.CounterKey;
29import org.graalvm.compiler.debug.DebugContext;
30import org.graalvm.compiler.nodes.ValueNode;
31import org.graalvm.compiler.nodes.java.MonitorIdNode;
32import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
33import org.graalvm.compiler.nodes.virtual.LockState;
34import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
35import org.graalvm.compiler.virtual.nodes.MaterializedObjectState;
36import org.graalvm.compiler.virtual.nodes.VirtualObjectState;
37
38import jdk.vm.ci.meta.JavaConstant;
39
40/**
41 * This class describes the state of a virtual object while iterating over the graph. It describes
42 * the fields or array elements (called "entries") and the lock count if the object is still
43 * virtual. If the object was materialized, it contains the current materialized value.
44 */
45public class ObjectState {
46
47    public static final CounterKey CREATE_ESCAPED_OBJECT_STATE = DebugContext.counter("CreateEscapeObjectState");
48    public static final CounterKey GET_ESCAPED_OBJECT_STATE = DebugContext.counter("GetEscapeObjectState");
49
50    private ValueNode[] entries;
51    private ValueNode materializedValue;
52    private LockState locks;
53    private boolean ensureVirtualized;
54
55    private EscapeObjectState cachedState;
56
57    /**
58     * ObjectStates are duplicated lazily, if this field is true then the state needs to be copied
59     * before it is modified.
60     */
61    boolean copyOnWrite;
62
63    public ObjectState(ValueNode[] entries, List<MonitorIdNode> locks, boolean ensureVirtualized) {
64        this(entries, (LockState) null, ensureVirtualized);
65        for (int i = locks.size() - 1; i >= 0; i--) {
66            this.locks = new LockState(locks.get(i), this.locks);
67        }
68    }
69
70    public ObjectState(ValueNode[] entries, LockState locks, boolean ensureVirtualized) {
71        this.entries = entries;
72        this.locks = locks;
73        this.ensureVirtualized = ensureVirtualized;
74    }
75
76    public ObjectState(ValueNode materializedValue, LockState locks, boolean ensureVirtualized) {
77        assert materializedValue != null;
78        this.materializedValue = materializedValue;
79        this.locks = locks;
80        this.ensureVirtualized = ensureVirtualized;
81    }
82
83    private ObjectState(ObjectState other) {
84        entries = other.entries == null ? null : other.entries.clone();
85        materializedValue = other.materializedValue;
86        locks = other.locks;
87        cachedState = other.cachedState;
88        ensureVirtualized = other.ensureVirtualized;
89    }
90
91    public ObjectState cloneState() {
92        return new ObjectState(this);
93    }
94
95    public EscapeObjectState createEscapeObjectState(DebugContext debug, VirtualObjectNode virtual) {
96        GET_ESCAPED_OBJECT_STATE.increment(debug);
97        if (cachedState == null) {
98            CREATE_ESCAPED_OBJECT_STATE.increment(debug);
99            if (isVirtual()) {
100                /*
101                 * Clear out entries that are default values anyway.
102                 *
103                 * TODO: this should be propagated into ObjectState.entries, but that will take some
104                 * more refactoring.
105                 */
106                ValueNode[] newEntries = entries.clone();
107                for (int i = 0; i < newEntries.length; i++) {
108                    if (newEntries[i].asJavaConstant() == JavaConstant.defaultForKind(virtual.entryKind(i).getStackKind())) {
109                        newEntries[i] = null;
110                    }
111                }
112                cachedState = new VirtualObjectState(virtual, newEntries);
113            } else {
114                cachedState = new MaterializedObjectState(virtual, materializedValue);
115            }
116        }
117        return cachedState;
118
119    }
120
121    public boolean isVirtual() {
122        assert materializedValue == null ^ entries == null;
123        return materializedValue == null;
124    }
125
126    /**
127     * Users of this method are not allowed to change the entries of the returned array.
128     */
129    public ValueNode[] getEntries() {
130        assert isVirtual();
131        return entries;
132    }
133
134    public ValueNode getEntry(int index) {
135        assert isVirtual();
136        return entries[index];
137    }
138
139    public ValueNode getMaterializedValue() {
140        assert !isVirtual();
141        return materializedValue;
142    }
143
144    public void setEntry(int index, ValueNode value) {
145        assert isVirtual();
146        cachedState = null;
147        entries[index] = value;
148    }
149
150    public void escape(ValueNode materialized) {
151        assert isVirtual();
152        assert materialized != null;
153        materializedValue = materialized;
154        entries = null;
155        cachedState = null;
156        assert !isVirtual();
157    }
158
159    public void updateMaterializedValue(ValueNode value) {
160        assert !isVirtual();
161        assert value != null;
162        cachedState = null;
163        materializedValue = value;
164    }
165
166    public void addLock(MonitorIdNode monitorId) {
167        locks = new LockState(monitorId, locks);
168    }
169
170    public MonitorIdNode removeLock() {
171        try {
172            return locks.monitorId;
173        } finally {
174            locks = locks.next;
175        }
176    }
177
178    public LockState getLocks() {
179        return locks;
180    }
181
182    public boolean hasLocks() {
183        return locks != null;
184    }
185
186    public boolean locksEqual(ObjectState other) {
187        LockState a = locks;
188        LockState b = other.locks;
189        while (a != null && b != null && a.monitorId == b.monitorId) {
190            a = a.next;
191            b = b.next;
192        }
193        return a == null && b == null;
194    }
195
196    public void setEnsureVirtualized(boolean ensureVirtualized) {
197        this.ensureVirtualized = ensureVirtualized;
198    }
199
200    public boolean getEnsureVirtualized() {
201        return ensureVirtualized;
202    }
203
204    @Override
205    public String toString() {
206        StringBuilder str = new StringBuilder().append('{');
207        if (locks != null) {
208            str.append('l').append(locks).append(' ');
209        }
210        if (entries != null) {
211            for (int i = 0; i < entries.length; i++) {
212                str.append("entry").append(i).append('=').append(entries[i]).append(' ');
213            }
214        }
215        if (materializedValue != null) {
216            str.append("mat=").append(materializedValue);
217        }
218
219        return str.append('}').toString();
220    }
221
222    @Override
223    public int hashCode() {
224        final int prime = 31;
225        int result = 1;
226        result = prime * result + Arrays.hashCode(entries);
227        result = prime * result + (locks != null ? locks.monitorId.getLockDepth() : 0);
228        result = prime * result + ((materializedValue == null) ? 0 : materializedValue.hashCode());
229        return result;
230    }
231
232    @Override
233    public boolean equals(Object obj) {
234        if (this == obj) {
235            return true;
236        }
237        if (obj == null || getClass() != obj.getClass()) {
238            return false;
239        }
240        ObjectState other = (ObjectState) obj;
241        if (!Arrays.equals(entries, other.entries)) {
242            return false;
243        }
244        if (!locksEqual(other)) {
245            return false;
246        }
247        if (materializedValue == null) {
248            if (other.materializedValue != null) {
249                return false;
250            }
251        } else if (!materializedValue.equals(other.materializedValue)) {
252            return false;
253        }
254        return true;
255    }
256
257    public ObjectState share() {
258        copyOnWrite = true;
259        return this;
260    }
261}
262