1 /*
   2  * Copyright (c) 2011, 2016, 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 package org.graalvm.compiler.nodes.extended;
  24 
  25 import static org.graalvm.compiler.nodeinfo.InputType.Memory;
  26 import static org.graalvm.compiler.nodeinfo.InputType.State;
  27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
  28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_20;
  29 
  30 import java.util.List;
  31 
  32 import org.graalvm.compiler.core.common.LocationIdentity;
  33 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  34 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
  35 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
  36 import org.graalvm.compiler.core.common.type.Stamp;
  37 import org.graalvm.compiler.core.common.type.StampFactory;
  38 import org.graalvm.compiler.graph.NodeClass;
  39 import org.graalvm.compiler.graph.NodeInputList;
  40 import org.graalvm.compiler.nodeinfo.NodeInfo;
  41 import org.graalvm.compiler.nodeinfo.Verbosity;
  42 import org.graalvm.compiler.nodes.DeoptimizingNode;
  43 import org.graalvm.compiler.nodes.FrameState;
  44 import org.graalvm.compiler.nodes.ValueNode;
  45 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  46 import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
  47 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
  48 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  49 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  50 
  51 import jdk.vm.ci.code.BytecodeFrame;
  52 import jdk.vm.ci.meta.JavaKind;
  53 import jdk.vm.ci.meta.ResolvedJavaMethod;
  54 import jdk.vm.ci.meta.ResolvedJavaType;
  55 import jdk.vm.ci.meta.Value;
  56 
  57 /**
  58  * Node for a {@linkplain ForeignCallDescriptor foreign} call.
  59  */
  60 // @formatter:off
  61 @NodeInfo(nameTemplate = "ForeignCall#{p#descriptor/s}",
  62           allowedUsageTypes = Memory,
  63           cycles = CYCLES_UNKNOWN,
  64           cyclesRationale = "A foreign call is a block box in terms of time spent in the callee.",
  65           size = SIZE_20)
  66 // @formatter:on
  67 public class ForeignCallNode extends AbstractMemoryCheckpoint implements LIRLowerable, DeoptimizingNode.DeoptDuring, MemoryCheckpoint.Multi {
  68     public static final NodeClass<ForeignCallNode> TYPE = NodeClass.create(ForeignCallNode.class);
  69 
  70     @Input protected NodeInputList<ValueNode> arguments;
  71     @OptionalInput(State) protected FrameState stateDuring;
  72     protected final ForeignCallsProvider foreignCalls;
  73 
  74     protected final ForeignCallDescriptor descriptor;
  75     protected int bci = BytecodeFrame.UNKNOWN_BCI;
  76 
  77     public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod targetMethod, @InjectedNodeParameter Stamp returnStamp, @InjectedNodeParameter ForeignCallsProvider foreignCalls,
  78                     ForeignCallDescriptor descriptor, ValueNode... arguments) {
  79         ForeignCallNode node = new ForeignCallNode(foreignCalls, descriptor, arguments);
  80         node.setStamp(returnStamp);
  81 
  82         assert verifyDescriptor(b, targetMethod, descriptor);
  83 
  84         /*
  85          * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the case
  86          * that the foreign call can deoptimize. As with all deoptimization, we need a state in a
  87          * non-intrinsic method.
  88          */
  89         GraphBuilderContext nonIntrinsicAncestor = b.getNonIntrinsicAncestor();
  90         if (nonIntrinsicAncestor != null) {
  91             node.setBci(nonIntrinsicAncestor.bci());
  92         }
  93 
  94         JavaKind returnKind = targetMethod.getSignature().getReturnKind();
  95         if (returnKind == JavaKind.Void) {
  96             b.add(node);
  97         } else {
  98             b.addPush(returnKind, node);
  99         }
 100 
 101         return true;
 102     }
 103 
 104     static boolean verifyDescriptor(GraphBuilderContext b, ResolvedJavaMethod targetMethod, ForeignCallDescriptor descriptor) {
 105         int parameters = 1;
 106         for (Class<?> arg : descriptor.getArgumentTypes()) {
 107             ResolvedJavaType res = b.getMetaAccess().lookupJavaType(arg);
 108             ResolvedJavaType parameterType = (ResolvedJavaType) targetMethod.getSignature().getParameterType(parameters, targetMethod.getDeclaringClass());
 109             assert parameterType.equals(res) : descriptor + ": parameter " + parameters + " mismatch: " + res + " != " + parameterType;
 110             parameters++;
 111         }
 112         return true;
 113     }
 114 
 115     public ForeignCallNode(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, ValueNode... arguments) {
 116         this(TYPE, foreignCalls, descriptor, arguments);
 117     }
 118 
 119     public ForeignCallNode(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, Stamp stamp, List<ValueNode> arguments) {
 120         super(TYPE, stamp);
 121         this.arguments = new NodeInputList<>(this, arguments);
 122         this.descriptor = descriptor;
 123         this.foreignCalls = foreignCalls;
 124         assert descriptor.getArgumentTypes().length == this.arguments.size() : "wrong number of arguments to " + this;
 125     }
 126 
 127     public ForeignCallNode(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, Stamp stamp) {
 128         super(TYPE, stamp);
 129         this.arguments = new NodeInputList<>(this);
 130         this.descriptor = descriptor;
 131         this.foreignCalls = foreignCalls;
 132     }
 133 
 134     protected ForeignCallNode(NodeClass<? extends ForeignCallNode> c, ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, ValueNode... arguments) {
 135         super(c, StampFactory.forKind(JavaKind.fromJavaClass(descriptor.getResultType())));
 136         this.arguments = new NodeInputList<>(this, arguments);
 137         this.descriptor = descriptor;
 138         this.foreignCalls = foreignCalls;
 139         assert descriptor.getArgumentTypes().length == this.arguments.size() : "wrong number of arguments to " + this;
 140     }
 141 
 142     @Override
 143     public boolean hasSideEffect() {
 144         return !foreignCalls.isReexecutable(descriptor);
 145     }
 146 
 147     public ForeignCallDescriptor getDescriptor() {
 148         return descriptor;
 149     }
 150 
 151     @Override
 152     public LocationIdentity[] getLocationIdentities() {
 153         return foreignCalls.getKilledLocations(descriptor);
 154     }
 155 
 156     protected Value[] operands(NodeLIRBuilderTool gen) {
 157         Value[] operands = new Value[arguments.size()];
 158         for (int i = 0; i < operands.length; i++) {
 159             operands[i] = gen.operand(arguments.get(i));
 160         }
 161         return operands;
 162     }
 163 
 164     @Override
 165     public void generate(NodeLIRBuilderTool gen) {
 166         ForeignCallLinkage linkage = gen.getLIRGeneratorTool().getForeignCalls().lookupForeignCall(descriptor);
 167         Value[] operands = operands(gen);
 168         Value result = gen.getLIRGeneratorTool().emitForeignCall(linkage, gen.state(this), operands);
 169         if (result != null) {
 170             gen.setResult(this, result);
 171         }
 172     }
 173 
 174     @Override
 175     public void setStateAfter(FrameState x) {
 176         assert hasSideEffect() || x == null;
 177         super.setStateAfter(x);
 178     }
 179 
 180     @Override
 181     public FrameState stateDuring() {
 182         return stateDuring;
 183     }
 184 
 185     @Override
 186     public void setStateDuring(FrameState stateDuring) {
 187         updateUsages(this.stateDuring, stateDuring);
 188         this.stateDuring = stateDuring;
 189     }
 190 
 191     public int getBci() {
 192         return bci;
 193     }
 194 
 195     /**
 196      * Set the {@code bci} of the invoke bytecode for use when converting a stateAfter into a
 197      * stateDuring.
 198      */
 199     public void setBci(int bci) {
 200         assert this.bci == BytecodeFrame.UNKNOWN_BCI || this.bci == bci;
 201         this.bci = bci;
 202     }
 203 
 204     @Override
 205     public void computeStateDuring(FrameState currentStateAfter) {
 206         FrameState newStateDuring;
 207         if ((currentStateAfter.stackSize() > 0 && currentStateAfter.stackAt(currentStateAfter.stackSize() - 1) == this) ||
 208                         (currentStateAfter.stackSize() > 1 && currentStateAfter.stackAt(currentStateAfter.stackSize() - 2) == this)) {
 209             // The result of this call is on the top of stack, so roll back to the previous bci.
 210             assert bci != BytecodeFrame.UNKNOWN_BCI : this;
 211             newStateDuring = currentStateAfter.duplicateModifiedDuringCall(bci, this.getStackKind());
 212         } else {
 213             newStateDuring = currentStateAfter;
 214         }
 215         setStateDuring(newStateDuring);
 216     }
 217 
 218     @Override
 219     public String toString(Verbosity verbosity) {
 220         if (verbosity == Verbosity.Name) {
 221             return super.toString(verbosity) + "#" + descriptor;
 222         }
 223         return super.toString(verbosity);
 224     }
 225 
 226     @Override
 227     public boolean canDeoptimize() {
 228         return foreignCalls.canDeoptimize(descriptor);
 229     }
 230 
 231     public boolean isGuaranteedSafepoint() {
 232         return foreignCalls.isGuaranteedSafepoint(descriptor);
 233     }
 234 
 235     public NodeInputList<ValueNode> getArguments() {
 236         return arguments;
 237     }
 238 }