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  */
23 
24 
25 package org.graalvm.compiler.virtual.phases.ea;
26 
27 import static org.graalvm.compiler.core.common.GraalOptions.MaximumEscapeAnalysisArrayLength;
28 
29 import java.util.List;
30 
31 import org.graalvm.compiler.core.common.spi.ArrayOffsetProvider;
32 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
33 import org.graalvm.compiler.debug.DebugContext;
34 import org.graalvm.compiler.graph.Node;
35 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
36 import org.graalvm.compiler.nodes.ConstantNode;
37 import org.graalvm.compiler.nodes.FixedNode;
38 import org.graalvm.compiler.nodes.FixedWithNextNode;
39 import org.graalvm.compiler.nodes.NodeView;
40 import org.graalvm.compiler.nodes.ValueNode;
41 import org.graalvm.compiler.nodes.calc.FloatingNode;
42 import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode;
43 import org.graalvm.compiler.nodes.java.MonitorIdNode;
44 import org.graalvm.compiler.nodes.spi.LoweringProvider;
45 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
46 import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode;
47 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
48 import org.graalvm.compiler.options.OptionValues;
49 
50 import jdk.vm.ci.meta.Assumptions;
51 import jdk.vm.ci.meta.ConstantReflectionProvider;
52 import jdk.vm.ci.meta.JavaConstant;
53 import jdk.vm.ci.meta.JavaKind;
54 import jdk.vm.ci.meta.MetaAccessProvider;
55 
56 /**
57  * Forwards calls from {@link VirtualizerTool} to the actual {@link PartialEscapeBlockState}.
58  */
59 class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool {
60 
61     private final MetaAccessProvider metaAccess;
62     private final ConstantReflectionProvider constantReflection;
63     private final ConstantFieldProvider constantFieldProvider;
64     private final PartialEscapeClosure<?> closure;
65     private final Assumptions assumptions;
66     private final OptionValues options;
67     private final DebugContext debug;
68     private final LoweringProvider loweringProvider;
69     private ConstantNode illegalConstant;
70 
VirtualizerToolImpl(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, PartialEscapeClosure<?> closure, Assumptions assumptions, OptionValues options, DebugContext debug, LoweringProvider loweringProvider)71     VirtualizerToolImpl(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, PartialEscapeClosure<?> closure,
72                     Assumptions assumptions, OptionValues options, DebugContext debug, LoweringProvider loweringProvider) {
73         this.metaAccess = metaAccess;
74         this.constantReflection = constantReflection;
75         this.constantFieldProvider = constantFieldProvider;
76         this.closure = closure;
77         this.assumptions = assumptions;
78         this.options = options;
79         this.debug = debug;
80         this.loweringProvider = loweringProvider;
81     }
82 
83     private boolean deleted;
84     private PartialEscapeBlockState<?> state;
85     private ValueNode current;
86     private FixedNode position;
87     private GraphEffectList effects;
88 
89     @Override
getOptions()90     public OptionValues getOptions() {
91         return options;
92     }
93 
94     @Override
getDebug()95     public DebugContext getDebug() {
96         return debug;
97     }
98 
99     @Override
getMetaAccessProvider()100     public MetaAccessProvider getMetaAccessProvider() {
101         return metaAccess;
102     }
103 
104     @Override
getConstantReflectionProvider()105     public ConstantReflectionProvider getConstantReflectionProvider() {
106         return constantReflection;
107     }
108 
109     @Override
getConstantFieldProvider()110     public ConstantFieldProvider getConstantFieldProvider() {
111         return constantFieldProvider;
112     }
113 
114     @Override
getArrayOffsetProvider()115     public ArrayOffsetProvider getArrayOffsetProvider() {
116         return loweringProvider;
117     }
118 
reset(PartialEscapeBlockState<?> newState, ValueNode newCurrent, FixedNode newPosition, GraphEffectList newEffects)119     public void reset(PartialEscapeBlockState<?> newState, ValueNode newCurrent, FixedNode newPosition, GraphEffectList newEffects) {
120         deleted = false;
121         state = newState;
122         current = newCurrent;
123         position = newPosition;
124         effects = newEffects;
125     }
126 
isDeleted()127     public boolean isDeleted() {
128         return deleted;
129     }
130 
131     @Override
getAlias(ValueNode value)132     public ValueNode getAlias(ValueNode value) {
133         return closure.getAliasAndResolve(state, value);
134     }
135 
136     @Override
getEntry(VirtualObjectNode virtualObject, int index)137     public ValueNode getEntry(VirtualObjectNode virtualObject, int index) {
138         return state.getObjectState(virtualObject).getEntry(index);
139     }
140 
141     @Override
setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value, JavaKind theAccessKind, long offset)142     public boolean setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value, JavaKind theAccessKind, long offset) {
143         ObjectState obj = state.getObjectState(virtual);
144         assert obj.isVirtual() : "not virtual: " + obj;
145         ValueNode newValue;
146         JavaKind entryKind = virtual.entryKind(index);
147         JavaKind accessKind = theAccessKind != null ? theAccessKind : entryKind;
148         if (value == null) {
149             newValue = null;
150         } else {
151             newValue = closure.getAliasAndResolve(state, value);
152         }
153         getDebug().log(DebugContext.DETAILED_LEVEL, "Setting entry %d in virtual object %s %s results in %s", index, virtual.getObjectId(), virtual, state.getObjectState(virtual.getObjectId()));
154         ValueNode oldValue = getEntry(virtual, index);
155         boolean canVirtualize = entryKind == accessKind || (entryKind == accessKind.getStackKind() && virtual instanceof VirtualInstanceNode);
156         if (!canVirtualize) {
157             if (entryKind == JavaKind.Long && oldValue.getStackKind() == newValue.getStackKind() && oldValue.getStackKind().isPrimitive()) {
158                 /*
159                  * Special case: If the entryKind is long, allow arbitrary kinds as long as a value
160                  * of the same kind is already there. This can only happen if some other node
161                  * initialized the entry with a value of a different kind. One example where this
162                  * happens is the Truffle NewFrameNode.
163                  */
164                 getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s with primitive of kind %s in long entry ", current, oldValue.getStackKind());
165                 canVirtualize = true;
166             } else if (entryKind == JavaKind.Int && (accessKind == JavaKind.Long || accessKind == JavaKind.Double) && offset % 8 == 0) {
167                 /*
168                  * Special case: Allow storing a single long or double value into two consecutive
169                  * int slots.
170                  */
171                 int nextIndex = virtual.entryIndexForOffset(getArrayOffsetProvider(), offset + 4, JavaKind.Int);
172                 if (nextIndex != -1) {
173                     canVirtualize = true;
174                     assert nextIndex == index + 1 : "expected to be sequential";
175                     getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s for double word stored in two ints", current);
176                 }
177             }
178         }
179 
180         if (canVirtualize) {
181             getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s for entryKind %s and access kind %s", current, entryKind, accessKind);
182             state.setEntry(virtual.getObjectId(), index, newValue);
183             if (entryKind == JavaKind.Int) {
184                 if (accessKind.needsTwoSlots()) {
185                     // Storing double word value two int slots
186                     assert virtual.entryKind(index + 1) == JavaKind.Int;
187                     state.setEntry(virtual.getObjectId(), index + 1, getIllegalConstant());
188                 } else if (oldValue.getStackKind() == JavaKind.Double || oldValue.getStackKind() == JavaKind.Long) {
189                     // Splitting double word constant by storing over it with an int
190                     getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing second half of double word value %s", current, oldValue);
191                     ValueNode secondHalf = UnpackEndianHalfNode.create(oldValue, false, NodeView.DEFAULT);
192                     addNode(secondHalf);
193                     state.setEntry(virtual.getObjectId(), index + 1, secondHalf);
194                 }
195             }
196             if (oldValue.isConstant() && oldValue.asConstant().equals(JavaConstant.forIllegal())) {
197                 // Storing into second half of double, so replace previous value
198                 ValueNode previous = getEntry(virtual, index - 1);
199                 getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing first half of double word value %s", current, previous);
200                 ValueNode firstHalf = UnpackEndianHalfNode.create(previous, true, NodeView.DEFAULT);
201                 addNode(firstHalf);
202                 state.setEntry(virtual.getObjectId(), index - 1, firstHalf);
203             }
204             return true;
205         }
206         // Should only occur if there are mismatches between the entry and access kind
207         assert entryKind != accessKind;
208         return false;
209     }
210 
getIllegalConstant()211     private ValueNode getIllegalConstant() {
212         if (illegalConstant == null) {
213             illegalConstant = ConstantNode.forConstant(JavaConstant.forIllegal(), getMetaAccessProvider());
214             addNode(illegalConstant);
215         }
216         return illegalConstant;
217     }
218 
219     @Override
setEnsureVirtualized(VirtualObjectNode virtualObject, boolean ensureVirtualized)220     public void setEnsureVirtualized(VirtualObjectNode virtualObject, boolean ensureVirtualized) {
221         int id = virtualObject.getObjectId();
222         state.setEnsureVirtualized(id, ensureVirtualized);
223     }
224 
225     @Override
getEnsureVirtualized(VirtualObjectNode virtualObject)226     public boolean getEnsureVirtualized(VirtualObjectNode virtualObject) {
227         return state.getObjectState(virtualObject).getEnsureVirtualized();
228     }
229 
230     @Override
replaceWithVirtual(VirtualObjectNode virtual)231     public void replaceWithVirtual(VirtualObjectNode virtual) {
232         closure.addVirtualAlias(virtual, current);
233         effects.deleteNode(current);
234         deleted = true;
235     }
236 
237     @Override
replaceWithValue(ValueNode replacement)238     public void replaceWithValue(ValueNode replacement) {
239         effects.replaceAtUsages(current, closure.getScalarAlias(replacement), position);
240         closure.addScalarAlias(current, replacement);
241         deleted = true;
242     }
243 
244     @Override
delete()245     public void delete() {
246         effects.deleteNode(current);
247         deleted = true;
248     }
249 
250     @Override
replaceFirstInput(Node oldInput, Node replacement)251     public void replaceFirstInput(Node oldInput, Node replacement) {
252         effects.replaceFirstInput(current, oldInput, replacement);
253     }
254 
255     @Override
addNode(ValueNode node)256     public void addNode(ValueNode node) {
257         if (node instanceof FloatingNode) {
258             effects.addFloatingNode(node, "VirtualizerTool");
259         } else {
260             effects.addFixedNodeBefore((FixedWithNextNode) node, position);
261         }
262     }
263 
264     @Override
createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, List<MonitorIdNode> locks, boolean ensureVirtualized)265     public void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, List<MonitorIdNode> locks, boolean ensureVirtualized) {
266         VirtualUtil.trace(options, debug, "{{%s}} ", current);
267         if (!virtualObject.isAlive()) {
268             effects.addFloatingNode(virtualObject, "newVirtualObject");
269         }
270         for (int i = 0; i < entryState.length; i++) {
271             ValueNode entry = entryState[i];
272             entryState[i] = entry instanceof VirtualObjectNode ? entry : closure.getAliasAndResolve(state, entry);
273         }
274         int id = virtualObject.getObjectId();
275         if (id == -1) {
276             id = closure.virtualObjects.size();
277             closure.virtualObjects.add(virtualObject);
278             virtualObject.setObjectId(id);
279         }
280         state.addObject(id, new ObjectState(entryState, locks, ensureVirtualized));
281         closure.addVirtualAlias(virtualObject, virtualObject);
282         PartialEscapeClosure.COUNTER_ALLOCATION_REMOVED.increment(debug);
283         effects.addVirtualizationDelta(1);
284     }
285 
286     @Override
getMaximumEntryCount()287     public int getMaximumEntryCount() {
288         return MaximumEscapeAnalysisArrayLength.getValue(current.getOptions());
289     }
290 
291     @Override
replaceWith(ValueNode node)292     public void replaceWith(ValueNode node) {
293         if (node instanceof VirtualObjectNode) {
294             replaceWithVirtual((VirtualObjectNode) node);
295         } else {
296             replaceWithValue(node);
297         }
298     }
299 
300     @Override
ensureMaterialized(VirtualObjectNode virtualObject)301     public boolean ensureMaterialized(VirtualObjectNode virtualObject) {
302         return closure.ensureMaterialized(state, virtualObject.getObjectId(), position, effects, PartialEscapeClosure.COUNTER_MATERIALIZATIONS_UNHANDLED);
303     }
304 
305     @Override
addLock(VirtualObjectNode virtualObject, MonitorIdNode monitorId)306     public void addLock(VirtualObjectNode virtualObject, MonitorIdNode monitorId) {
307         int id = virtualObject.getObjectId();
308         state.addLock(id, monitorId);
309     }
310 
311     @Override
removeLock(VirtualObjectNode virtualObject)312     public MonitorIdNode removeLock(VirtualObjectNode virtualObject) {
313         int id = virtualObject.getObjectId();
314         return state.removeLock(id);
315     }
316 
317     @Override
getMetaAccess()318     public MetaAccessProvider getMetaAccess() {
319         return metaAccess;
320     }
321 
322     @Override
getConstantReflection()323     public ConstantReflectionProvider getConstantReflection() {
324         return constantReflection;
325     }
326 
327     @Override
canonicalizeReads()328     public boolean canonicalizeReads() {
329         return false;
330     }
331 
332     @Override
allUsagesAvailable()333     public boolean allUsagesAvailable() {
334         return true;
335     }
336 
337     @Override
getAssumptions()338     public Assumptions getAssumptions() {
339         return assumptions;
340     }
341 
342     @Override
smallestCompareWidth()343     public Integer smallestCompareWidth() {
344         if (loweringProvider != null) {
345             return loweringProvider.smallestCompareWidth();
346         } else {
347             return null;
348         }
349     }
350 }
351