/* * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.truffle.nodes.frame; import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.TypeReference; import org.graalvm.compiler.graph.IterableNodeType; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; import org.graalvm.compiler.nodes.spi.VirtualizableAllocation; import org.graalvm.compiler.nodes.spi.VirtualizerTool; import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.truffle.GraalTruffleRuntime; import org.graalvm.compiler.truffle.OptimizedAssumption; import org.graalvm.compiler.truffle.OptimizedCallTarget; import org.graalvm.compiler.truffle.nodes.AssumptionValidAssumption; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlotKind; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; /** * Intrinsic node representing the call for creating a frame in the {@link OptimizedCallTarget} * class. */ @NodeInfo(cycles = CYCLES_0, size = SIZE_0) public final class NewFrameNode extends FixedWithNextNode implements IterableNodeType, VirtualizableAllocation, Canonicalizable { public static final NodeClass<NewFrameNode> TYPE = NodeClass.create(NewFrameNode.class); @Input ValueNode descriptor; @Input ValueNode arguments; @Input VirtualObjectNode virtualFrame; @Input VirtualObjectNode virtualFrameObjectArray; @OptionalInput VirtualObjectNode virtualFramePrimitiveArray; @OptionalInput VirtualObjectNode virtualFrameTagArray; @Input NodeInputList<ValueNode> smallIntConstants; @Input private ValueNode frameDefaultValue; private final boolean intrinsifyAccessors; private final FrameSlot[] frameSlots; private final SpeculationReason intrinsifyAccessorsSpeculation; static final class IntrinsifyFrameAccessorsSpeculationReason implements SpeculationReason { private final FrameDescriptor frameDescriptor; IntrinsifyFrameAccessorsSpeculationReason(FrameDescriptor frameDescriptor) { this.frameDescriptor = frameDescriptor; } @Override public boolean equals(Object obj) { return obj instanceof IntrinsifyFrameAccessorsSpeculationReason && ((IntrinsifyFrameAccessorsSpeculationReason) obj).frameDescriptor == this.frameDescriptor; } @Override public int hashCode() { return System.identityHashCode(frameDescriptor); } } public NewFrameNode(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, StructuredGraph graph, ResolvedJavaType frameType, FrameDescriptor frameDescriptor, ValueNode frameDescriptorNode, ValueNode arguments) { super(TYPE, StampFactory.objectNonNull(TypeReference.createExactTrusted(frameType))); this.descriptor = frameDescriptorNode; this.arguments = arguments; /* * We access the FrameDescriptor only here and copy out all relevant data. So later * modifications to the FrameDescriptor by the running Truffle thread do not interfere. The * frame version assumption is registered first, so that we get invalidated in case the * FrameDescriptor changes. */ graph.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) frameDescriptor.getVersion())); /* * We only want to intrinsify get/set/is accessor methods of a virtual frame when we expect * that the frame is not going to be materialized. Materialization results in heap-based * data arrays, which means that set-methods need a FrameState. Most of the benefit of * accessor method intrinsification is avoding the FrameState creation during partial * evaluation. */ this.intrinsifyAccessorsSpeculation = new IntrinsifyFrameAccessorsSpeculationReason(frameDescriptor); this.intrinsifyAccessors = !GraalTruffleRuntime.getRuntime().getFrameMaterializeCalled(frameDescriptor) && graph.getSpeculationLog().maySpeculate(intrinsifyAccessorsSpeculation); this.frameDefaultValue = ConstantNode.forConstant(snippetReflection.forObject(frameDescriptor.getDefaultValue()), metaAccess, graph); this.frameSlots = frameDescriptor.getSlots().toArray(new FrameSlot[0]); ResolvedJavaField[] frameFields = frameType.getInstanceFields(true); ResolvedJavaField localsField = findField(frameFields, "locals"); ResolvedJavaField primitiveLocalsField = findField(frameFields, "primitiveLocals"); ResolvedJavaField tagsField = findField(frameFields, "tags"); this.virtualFrame = graph.add(new VirtualInstanceNode(frameType, frameFields, true)); this.virtualFrameObjectArray = graph.add(new VirtualArrayNode((ResolvedJavaType) localsField.getType().getComponentType(), frameSlots.length)); if (primitiveLocalsField != null) { this.virtualFramePrimitiveArray = graph.add(new VirtualArrayNode((ResolvedJavaType) primitiveLocalsField.getType().getComponentType(), frameSlots.length)); this.virtualFrameTagArray = graph.add(new VirtualArrayNode((ResolvedJavaType) tagsField.getType().getComponentType(), frameSlots.length)); } ValueNode[] c = new ValueNode[FrameSlotKind.values().length]; for (int i = 0; i < c.length; i++) { c[i] = ConstantNode.forInt(i, graph); } this.smallIntConstants = new NodeInputList<>(this, c); } public ValueNode getDescriptor() { return descriptor; } public ValueNode getArguments() { return arguments; } public boolean getIntrinsifyAccessors() { return intrinsifyAccessors; } public SpeculationReason getIntrinsifyAccessorsSpeculation() { return intrinsifyAccessorsSpeculation; } private static ResolvedJavaField findField(ResolvedJavaField[] fields, String fieldName) { for (ResolvedJavaField field : fields) { if (field.getName().equals(fieldName)) { return field; } } return null; } @Override public void virtualize(VirtualizerTool tool) { int frameSize = frameSlots.length; ResolvedJavaType frameType = stamp().javaType(tool.getMetaAccessProvider()); ResolvedJavaField[] frameFields = frameType.getInstanceFields(true); ResolvedJavaField descriptorField = findField(frameFields, "descriptor"); ResolvedJavaField argumentsField = findField(frameFields, "arguments"); ResolvedJavaField localsField = findField(frameFields, "locals"); ResolvedJavaField primitiveLocalsField = findField(frameFields, "primitiveLocals"); ResolvedJavaField tagsField = findField(frameFields, "tags"); ValueNode[] objectArrayEntryState = new ValueNode[frameSize]; ValueNode[] primitiveArrayEntryState = new ValueNode[frameSize]; ValueNode[] tagArrayEntryState = new ValueNode[frameSize]; if (frameSize > 0) { Arrays.fill(objectArrayEntryState, frameDefaultValue); if (virtualFrameTagArray != null) { Arrays.fill(tagArrayEntryState, smallIntConstants.get(0)); } if (virtualFramePrimitiveArray != null) { for (int i = 0; i < frameSize; i++) { primitiveArrayEntryState[i] = initialPrimitiveValue(frameSlots[i].getKind()); } } } tool.createVirtualObject(virtualFrameObjectArray, objectArrayEntryState, Collections.<MonitorIdNode> emptyList(), false); if (virtualFramePrimitiveArray != null) { tool.createVirtualObject(virtualFramePrimitiveArray, primitiveArrayEntryState, Collections.<MonitorIdNode> emptyList(), false); } if (virtualFrameTagArray != null) { tool.createVirtualObject(virtualFrameTagArray, tagArrayEntryState, Collections.<MonitorIdNode> emptyList(), false); } assert frameFields.length == 5 || frameFields.length == 3; ValueNode[] frameEntryState = new ValueNode[frameFields.length]; List<ResolvedJavaField> frameFieldList = Arrays.asList(frameFields); frameEntryState[frameFieldList.indexOf(descriptorField)] = getDescriptor(); frameEntryState[frameFieldList.indexOf(argumentsField)] = getArguments(); frameEntryState[frameFieldList.indexOf(localsField)] = virtualFrameObjectArray; if (primitiveLocalsField != null) { frameEntryState[frameFieldList.indexOf(primitiveLocalsField)] = virtualFramePrimitiveArray; } if (tagsField != null) { frameEntryState[frameFieldList.indexOf(tagsField)] = virtualFrameTagArray; } /* * The new frame is created with "ensureVirtualized" enabled, so that it cannot be * materialized. This can only be lifted by a AllowMaterializeNode, which corresponds to a * frame.materialize() call. */ tool.createVirtualObject(virtualFrame, frameEntryState, Collections.<MonitorIdNode> emptyList(), true); tool.replaceWithVirtual(virtualFrame); } private ValueNode initialPrimitiveValue(FrameSlotKind kind) { JavaKind graalKind = null; switch (kind) { case Boolean: case Byte: case Int: graalKind = JavaKind.Int; break; case Double: graalKind = JavaKind.Double; break; case Float: graalKind = JavaKind.Float; break; case Long: graalKind = JavaKind.Long; break; case Object: case Illegal: // won't be stored in the primitive array, so default to long graalKind = JavaKind.Long; break; default: throw new IllegalStateException("Unexpected frame slot kind: " + kind); } return ConstantNode.defaultForKind(graalKind, graph()); } @Override public Node canonical(CanonicalizerTool tool) { if (tool.allUsagesAvailable() && hasNoUsages()) { return null; } else { return this; } } }