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