001/*
002 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.replacements;
024
025import static com.oracle.graal.replacements.NodeIntrinsificationPhase.*;
026import static jdk.internal.jvmci.meta.MetaUtil.*;
027
028import java.util.*;
029
030import jdk.internal.jvmci.common.*;
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.api.replacements.*;
034import com.oracle.graal.compiler.common.type.*;
035import com.oracle.graal.debug.*;
036import com.oracle.graal.graph.Node.NodeIntrinsic;
037import com.oracle.graal.graphbuilderconf.*;
038import com.oracle.graal.nodeinfo.*;
039import com.oracle.graal.nodeinfo.StructuralInput.MarkerType;
040import com.oracle.graal.nodes.*;
041import com.oracle.graal.nodes.extended.*;
042import com.oracle.graal.word.*;
043
044/**
045 * An {@link NodePlugin} that handles methods annotated by {@link Fold} and {@link NodeIntrinsic}.
046 */
047public class NodeIntrinsificationPlugin implements NodePlugin {
048    protected final NodeIntrinsificationPhase nodeIntrinsification;
049    private final WordTypes wordTypes;
050    private final ResolvedJavaType structuralInputType;
051    private final boolean mustIntrinsify;
052
053    public NodeIntrinsificationPlugin(MetaAccessProvider metaAccess, NodeIntrinsificationPhase nodeIntrinsification, WordTypes wordTypes, boolean mustIntrinsify) {
054        this.nodeIntrinsification = nodeIntrinsification;
055        this.wordTypes = wordTypes;
056        this.mustIntrinsify = mustIntrinsify;
057        this.structuralInputType = metaAccess.lookupJavaType(StructuralInput.class);
058    }
059
060    /**
061     * Calls in replacements to methods matching one of these filters are elided. Only void methods
062     * are considered for elision. The use of "snippets" in name of the variable and system property
063     * is purely for legacy reasons.
064     */
065    private static final MethodFilter[] MethodsElidedInSnippets = getMethodsElidedInSnippets();
066
067    private static MethodFilter[] getMethodsElidedInSnippets() {
068        String commaSeparatedPatterns = System.getProperty("graal.MethodsElidedInSnippets");
069        if (commaSeparatedPatterns != null) {
070            return MethodFilter.parse(commaSeparatedPatterns);
071        }
072        return null;
073    }
074
075    @Override
076    public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
077        NodeIntrinsic intrinsic = nodeIntrinsification.getIntrinsic(method);
078        if (intrinsic != null) {
079            Signature sig = method.getSignature();
080            Kind returnKind = sig.getReturnKind();
081            Stamp stamp = StampFactory.forKind(returnKind);
082            if (returnKind == Kind.Object) {
083                JavaType returnType = sig.getReturnType(method.getDeclaringClass());
084                if (returnType instanceof ResolvedJavaType) {
085                    ResolvedJavaType resolvedReturnType = (ResolvedJavaType) returnType;
086                    if (wordTypes.isWord(resolvedReturnType)) {
087                        stamp = wordTypes.getWordStamp(resolvedReturnType);
088                    } else {
089                        stamp = StampFactory.declared(resolvedReturnType);
090                    }
091                }
092            }
093
094            boolean result = processNodeIntrinsic(b, method, intrinsic, Arrays.asList(args), returnKind, stamp);
095            if (!result && mustIntrinsify) {
096                reportIntrinsificationFailure(b, method, args);
097            }
098            return result;
099
100        } else if (nodeIntrinsification.isFoldable(method)) {
101            ResolvedJavaType[] parameterTypes = resolveJavaTypes(method.toParameterTypes(), method.getDeclaringClass());
102            JavaConstant constant = nodeIntrinsification.tryFold(Arrays.asList(args), parameterTypes, method);
103            if (!COULD_NOT_FOLD.equals(constant)) {
104                if (constant != null) {
105                    // Replace the invoke with the result of the call
106                    b.push(method.getSignature().getReturnKind(), ConstantNode.forConstant(constant, b.getMetaAccess(), b.getGraph()));
107                } else {
108                    // This must be a void invoke
109                    assert method.getSignature().getReturnKind() == Kind.Void;
110                }
111                return true;
112            } else if (mustIntrinsify) {
113                reportIntrinsificationFailure(b, method, args);
114            }
115
116        } else if (MethodsElidedInSnippets != null) {
117            if (MethodFilter.matches(MethodsElidedInSnippets, method)) {
118                if (method.getSignature().getReturnKind() != Kind.Void) {
119                    throw new JVMCIError("Cannot elide non-void method " + method.format("%H.%n(%p)"));
120                }
121                return true;
122            }
123        }
124        return false;
125    }
126
127    private static boolean reportIntrinsificationFailure(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
128        StringBuilder msg = new StringBuilder();
129        msg.append("Call in ").append(b.getMethod().format("%H.%n(%p)"));
130        msg.append(" to ").append(method.format("%H.%n(%p)"));
131        msg.append(" cannot be intrisfied or folded, probably because an argument is not a constant. Arguments: ");
132        String sep = "";
133        for (ValueNode node : args) {
134            msg.append(sep).append(node.toString());
135            sep = ", ";
136        }
137        throw new JVMCIError(msg.toString());
138    }
139
140    private InputType getInputType(ResolvedJavaType type) {
141        if (type != null && structuralInputType.isAssignableFrom(type)) {
142            MarkerType markerType = type.getAnnotation(MarkerType.class);
143            if (markerType != null) {
144                return markerType.value();
145            } else {
146                throw JVMCIError.shouldNotReachHere(String.format("%s extends StructuralInput, but is not annotated with @MarkerType", type));
147            }
148        } else {
149            return InputType.Value;
150        }
151    }
152
153    private boolean processNodeIntrinsic(GraphBuilderContext b, ResolvedJavaMethod method, NodeIntrinsic intrinsic, List<ValueNode> args, Kind returnKind, Stamp stamp) {
154        ValueNode res = createNodeIntrinsic(b, method, intrinsic, args, stamp);
155        if (res == null) {
156            return false;
157        }
158        if (res instanceof UnsafeCopyNode) {
159            UnsafeCopyNode copy = (UnsafeCopyNode) res;
160            UnsafeLoadNode value = b.add(new UnsafeLoadNode(copy.sourceObject(), copy.sourceOffset(), copy.accessKind(), copy.getLocationIdentity()));
161            b.add(new UnsafeStoreNode(copy.destinationObject(), copy.destinationOffset(), value, copy.accessKind(), copy.getLocationIdentity()));
162            return true;
163        } else if (res instanceof ForeignCallNode) {
164            /*
165             * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the
166             * case that the foreign call can deoptimize. As with all deoptimization, we need a
167             * state in a non-intrinsic method.
168             */
169            GraphBuilderContext nonIntrinsicAncestor = b.getNonIntrinsicAncestor();
170            if (nonIntrinsicAncestor != null) {
171                ForeignCallNode foreign = (ForeignCallNode) res;
172                foreign.setBci(nonIntrinsicAncestor.bci());
173            }
174        }
175
176        boolean nonValueType = false;
177        if (returnKind == Kind.Object && stamp instanceof ObjectStamp) {
178            ResolvedJavaType type = ((ObjectStamp) stamp).type();
179            if (type != null && structuralInputType.isAssignableFrom(type)) {
180                assert res.isAllowedUsageType(getInputType(type));
181                nonValueType = true;
182            }
183        }
184
185        if (returnKind != Kind.Void) {
186            assert nonValueType || res.getKind().getStackKind() != Kind.Void;
187            res = b.addPush(returnKind, res);
188        } else {
189            assert res.getKind().getStackKind() == Kind.Void;
190            res = b.add(res);
191        }
192
193        return true;
194    }
195
196    private ValueNode createNodeIntrinsic(GraphBuilderContext b, ResolvedJavaMethod method, NodeIntrinsic intrinsic, List<ValueNode> args, Stamp stamp) {
197        ValueNode res = nodeIntrinsification.createIntrinsicNode(args, stamp, method, b.getGraph(), intrinsic);
198        assert res != null : String.format("Could not create node intrinsic for call to %s as one of the arguments expected to be constant isn't: arguments=%s", method.format("%H.%n(%p)"), args);
199        return res;
200    }
201}