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 
  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
  90     public OptionValues getOptions() {
  91         return options;
  92     }
  93 
  94     @Override
  95     public DebugContext getDebug() {
  96         return debug;
  97     }
  98 
  99     @Override
 100     public MetaAccessProvider getMetaAccessProvider() {
 101         return metaAccess;
 102     }
 103 
 104     @Override
 105     public ConstantReflectionProvider getConstantReflectionProvider() {
 106         return constantReflection;
 107     }
 108 
 109     @Override
 110     public ConstantFieldProvider getConstantFieldProvider() {
 111         return constantFieldProvider;
 112     }
 113 
 114     @Override
 115     public ArrayOffsetProvider getArrayOffsetProvider() {
 116         return loweringProvider;
 117     }
 118 
 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 
 127     public boolean isDeleted() {
 128         return deleted;
 129     }
 130 
 131     @Override
 132     public ValueNode getAlias(ValueNode value) {
 133         return closure.getAliasAndResolve(state, value);
 134     }
 135 
 136     @Override
 137     public ValueNode getEntry(VirtualObjectNode virtualObject, int index) {
 138         return state.getObjectState(virtualObject).getEntry(index);
 139     }
 140 
 141     @Override
 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 
 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
 220     public void setEnsureVirtualized(VirtualObjectNode virtualObject, boolean ensureVirtualized) {
 221         int id = virtualObject.getObjectId();
 222         state.setEnsureVirtualized(id, ensureVirtualized);
 223     }
 224 
 225     @Override
 226     public boolean getEnsureVirtualized(VirtualObjectNode virtualObject) {
 227         return state.getObjectState(virtualObject).getEnsureVirtualized();
 228     }
 229 
 230     @Override
 231     public void replaceWithVirtual(VirtualObjectNode virtual) {
 232         closure.addVirtualAlias(virtual, current);
 233         effects.deleteNode(current);
 234         deleted = true;
 235     }
 236 
 237     @Override
 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
 245     public void delete() {
 246         effects.deleteNode(current);
 247         deleted = true;
 248     }
 249 
 250     @Override
 251     public void replaceFirstInput(Node oldInput, Node replacement) {
 252         effects.replaceFirstInput(current, oldInput, replacement);
 253     }
 254 
 255     @Override
 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
 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
 287     public int getMaximumEntryCount() {
 288         return MaximumEscapeAnalysisArrayLength.getValue(current.getOptions());
 289     }
 290 
 291     @Override
 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
 301     public boolean ensureMaterialized(VirtualObjectNode virtualObject) {
 302         return closure.ensureMaterialized(state, virtualObject.getObjectId(), position, effects, PartialEscapeClosure.COUNTER_MATERIALIZATIONS_UNHANDLED);
 303     }
 304 
 305     @Override
 306     public void addLock(VirtualObjectNode virtualObject, MonitorIdNode monitorId) {
 307         int id = virtualObject.getObjectId();
 308         state.addLock(id, monitorId);
 309     }
 310 
 311     @Override
 312     public MonitorIdNode removeLock(VirtualObjectNode virtualObject) {
 313         int id = virtualObject.getObjectId();
 314         return state.removeLock(id);
 315     }
 316 
 317     @Override
 318     public MetaAccessProvider getMetaAccess() {
 319         return metaAccess;
 320     }
 321 
 322     @Override
 323     public ConstantReflectionProvider getConstantReflection() {
 324         return constantReflection;
 325     }
 326 
 327     @Override
 328     public boolean canonicalizeReads() {
 329         return false;
 330     }
 331 
 332     @Override
 333     public boolean allUsagesAvailable() {
 334         return true;
 335     }
 336 
 337     @Override
 338     public Assumptions getAssumptions() {
 339         return assumptions;
 340     }
 341 
 342     @Override
 343     public Integer smallestCompareWidth() {
 344         if (loweringProvider != null) {
 345             return loweringProvider.smallestCompareWidth();
 346         } else {
 347             return null;
 348         }
 349     }
 350 }