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