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.truffle.substitutions;
024
025import static java.lang.Character.*;
026
027import java.util.concurrent.*;
028
029import jdk.internal.jvmci.meta.*;
030
031import com.oracle.graal.api.replacements.*;
032import com.oracle.graal.compiler.common.calc.*;
033import com.oracle.graal.compiler.common.type.*;
034import com.oracle.graal.graph.*;
035import com.oracle.graal.graphbuilderconf.*;
036import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver;
037import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
038import com.oracle.graal.nodes.*;
039import com.oracle.graal.nodes.calc.*;
040import com.oracle.graal.nodes.extended.*;
041import com.oracle.graal.replacements.nodes.arithmetic.*;
042import com.oracle.graal.truffle.*;
043import com.oracle.graal.truffle.nodes.*;
044import com.oracle.graal.truffle.nodes.asserts.*;
045import com.oracle.graal.truffle.nodes.frame.*;
046import com.oracle.graal.truffle.unsafe.*;
047import com.oracle.truffle.api.*;
048import com.oracle.truffle.api.frame.*;
049
050/**
051 * Provides {@link InvocationPlugin}s for Truffle classes.
052 */
053public class TruffleGraphBuilderPlugins {
054    public static void registerInvocationPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins, boolean canDelayIntrinsification, SnippetReflectionProvider snippetReflection) {
055
056        registerOptimizedAssumptionPlugins(plugins, snippetReflection);
057        registerExactMathPlugins(plugins);
058        registerCompilerDirectivesPlugins(plugins);
059        registerCompilerAssertsPlugins(plugins, canDelayIntrinsification);
060        registerOptimizedCallTargetPlugins(metaAccess, plugins);
061        registerUnsafeAccessImplPlugins(plugins, canDelayIntrinsification);
062
063        if (TruffleCompilerOptions.TruffleUseFrameWithoutBoxing.getValue()) {
064            registerFrameWithoutBoxingPlugins(plugins, canDelayIntrinsification);
065        } else {
066            registerFrameWithBoxingPlugins(plugins, canDelayIntrinsification);
067        }
068
069    }
070
071    public static void registerOptimizedAssumptionPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection) {
072        Registration r = new Registration(plugins, OptimizedAssumption.class);
073        InvocationPlugin plugin = new InvocationPlugin() {
074            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
075                if (receiver.isConstant()) {
076                    Constant constant = receiver.get().asConstant();
077                    OptimizedAssumption assumption = snippetReflection.asObject(OptimizedAssumption.class, (JavaConstant) constant);
078                    if (assumption.isValid()) {
079                        if (targetMethod.getName().equals("isValid")) {
080                            b.addPush(Kind.Boolean, ConstantNode.forBoolean(true));
081                        } else {
082                            assert targetMethod.getName().equals("check") : targetMethod;
083                        }
084                        b.getAssumptions().record(new AssumptionValidAssumption(assumption));
085                    } else {
086                        if (targetMethod.getName().equals("isValid")) {
087                            b.addPush(Kind.Boolean, ConstantNode.forBoolean(false));
088                        } else {
089                            assert targetMethod.getName().equals("check") : targetMethod;
090                            b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.None));
091                        }
092                    }
093                    return true;
094                } else {
095                    return false;
096                }
097            }
098        };
099        r.register1("isValid", Receiver.class, plugin);
100        r.register1("check", Receiver.class, plugin);
101    }
102
103    public static void registerExactMathPlugins(InvocationPlugins plugins) {
104        Registration r = new Registration(plugins, ExactMath.class);
105        for (Kind kind : new Kind[]{Kind.Int, Kind.Long}) {
106            Class<?> type = kind.toJavaClass();
107            r.register2("addExact", type, type, new InvocationPlugin() {
108                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
109                    b.addPush(kind, new IntegerAddExactNode(x, y));
110                    return true;
111                }
112            });
113            r.register2("subtractExact", type, type, new InvocationPlugin() {
114                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
115                    b.addPush(kind, new IntegerSubExactNode(x, y));
116                    return true;
117                }
118            });
119            r.register2("multiplyExact", type, type, new InvocationPlugin() {
120                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
121                    b.addPush(kind, new IntegerMulExactNode(x, y));
122                    return true;
123                }
124            });
125            r.register2("multiplyHigh", type, type, new InvocationPlugin() {
126                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
127                    b.addPush(kind, new IntegerMulHighNode(x, y));
128                    return true;
129                }
130            });
131            r.register2("multiplyHighUnsigned", type, type, new InvocationPlugin() {
132                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
133                    b.addPush(kind, new UnsignedMulHighNode(x, y));
134                    return true;
135                }
136            });
137        }
138    }
139
140    public static void registerCompilerDirectivesPlugins(InvocationPlugins plugins) {
141        Registration r = new Registration(plugins, CompilerDirectives.class);
142        r.register0("inInterpreter", new InvocationPlugin() {
143            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
144                b.addPush(Kind.Boolean, ConstantNode.forBoolean(false));
145                return true;
146            }
147        });
148        r.register0("inCompiledCode", new InvocationPlugin() {
149            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
150                b.addPush(Kind.Boolean, ConstantNode.forBoolean(true));
151                return true;
152            }
153        });
154        r.register0("transferToInterpreter", new InvocationPlugin() {
155            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
156                b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
157                return true;
158            }
159        });
160        r.register0("transferToInterpreterAndInvalidate", new InvocationPlugin() {
161            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
162                b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
163                return true;
164            }
165        });
166        r.register1("interpreterOnly", Runnable.class, new InvocationPlugin() {
167            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
168                return true;
169            }
170        });
171        r.register1("interpreterOnly", Callable.class, new InvocationPlugin() {
172            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
173                return true;
174            }
175        });
176        r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
177            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
178                b.addPush(Kind.Boolean, new BranchProbabilityNode(probability, condition));
179                return true;
180            }
181        });
182        r.register1("bailout", String.class, new InvocationPlugin() {
183            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
184                if (message.isConstant()) {
185                    throw b.bailout(message.asConstant().toValueString());
186                }
187                throw b.bailout("bailout (message is not compile-time constant, so no additional information is available)");
188            }
189        });
190        r.register1("isCompilationConstant", Object.class, new InvocationPlugin() {
191            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
192                if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) {
193                    b.addPush(Kind.Boolean, ConstantNode.forBoolean(true));
194                } else {
195                    b.addPush(Kind.Boolean, new IsCompilationConstantNode(value));
196                }
197                return true;
198            }
199        });
200        r.register1("materialize", Object.class, new InvocationPlugin() {
201            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
202                b.add(new ForceMaterializeNode(value));
203                return true;
204            }
205        });
206    }
207
208    public static void registerCompilerAssertsPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
209        Registration r = new Registration(plugins, CompilerAsserts.class);
210        r.register1("partialEvaluationConstant", Object.class, new InvocationPlugin() {
211            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
212                ValueNode curValue = value;
213                if (curValue instanceof BoxNode) {
214                    BoxNode boxNode = (BoxNode) curValue;
215                    curValue = boxNode.getValue();
216                }
217                if (curValue.isConstant()) {
218                    return true;
219                } else if (canDelayIntrinsification) {
220                    return false;
221                } else {
222                    StringBuilder sb = new StringBuilder();
223                    sb.append(curValue);
224                    if (curValue instanceof ValuePhiNode) {
225                        ValuePhiNode valuePhi = (ValuePhiNode) curValue;
226                        sb.append(" (");
227                        for (Node n : valuePhi.inputs()) {
228                            sb.append(n);
229                            sb.append("; ");
230                        }
231                        sb.append(")");
232                    }
233                    throw b.bailout("Partial evaluation did not reduce value to a constant, is a regular compiler node: " + sb.toString());
234                }
235            }
236        });
237        r.register0("neverPartOfCompilation", new InvocationPlugin() {
238            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
239                b.add(new NeverPartOfCompilationNode("CompilerAsserts.neverPartOfCompilation()"));
240                return true;
241            }
242        });
243        r.register1("neverPartOfCompilation", String.class, new InvocationPlugin() {
244            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
245                if (message.isConstant()) {
246                    String messageString = message.asConstant().toValueString();
247                    b.add(new NeverPartOfCompilationNode(messageString));
248                    return true;
249                } else {
250                    throw b.bailout("message for never part of compilation is non-constant");
251                }
252            }
253        });
254    }
255
256    public static void registerOptimizedCallTargetPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) {
257        Registration r = new Registration(plugins, OptimizedCallTarget.class);
258        r.register2("createFrame", FrameDescriptor.class, Object[].class, new InvocationPlugin() {
259            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode descriptor, ValueNode args) {
260                Class<?> frameClass = TruffleCompilerOptions.TruffleUseFrameWithoutBoxing.getValue() ? FrameWithoutBoxing.class : FrameWithBoxing.class;
261                b.addPush(Kind.Object, new NewFrameNode(StampFactory.exactNonNull(metaAccess.lookupJavaType(frameClass)), descriptor, args));
262                return true;
263            }
264        });
265        r.register2("castArrayFixedLength", Object[].class, int.class, new InvocationPlugin() {
266            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode args, ValueNode length) {
267                b.addPush(Kind.Object, new PiArrayNode(args, length, args.stamp()));
268                return true;
269            }
270        });
271    }
272
273    public static void registerFrameWithoutBoxingPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
274        Registration r = new Registration(plugins, FrameWithoutBoxing.class);
275        registerMaterialize(r);
276        registerUnsafeCast(r, canDelayIntrinsification);
277        registerUnsafeLoadStorePlugins(r, Kind.Int, Kind.Long, Kind.Float, Kind.Double, Kind.Object);
278    }
279
280    public static void registerFrameWithBoxingPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
281        Registration r = new Registration(plugins, FrameWithBoxing.class);
282        registerMaterialize(r);
283        registerUnsafeCast(r, canDelayIntrinsification);
284    }
285
286    public static void registerUnsafeAccessImplPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
287        Registration r = new Registration(plugins, UnsafeAccessImpl.class);
288        registerUnsafeCast(r, canDelayIntrinsification);
289        registerUnsafeLoadStorePlugins(r, Kind.Boolean, Kind.Byte, Kind.Int, Kind.Short, Kind.Long, Kind.Float, Kind.Double, Kind.Object);
290    }
291
292    private static void registerMaterialize(Registration r) {
293        r.register1("materialize", Receiver.class, new InvocationPlugin() {
294            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frame) {
295                b.addPush(Kind.Object, new MaterializeFrameNode(frame.get()));
296                return true;
297            }
298        });
299    }
300
301    private static void registerUnsafeCast(Registration r, boolean canDelayIntrinsification) {
302        r.register4("unsafeCast", Object.class, Class.class, boolean.class, boolean.class, new InvocationPlugin() {
303            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode clazz, ValueNode condition, ValueNode nonNull) {
304                if (clazz.isConstant() && nonNull.isConstant()) {
305                    ConstantReflectionProvider constantReflection = b.getConstantReflection();
306                    ResolvedJavaType javaType = constantReflection.asJavaType(clazz.asConstant());
307                    if (javaType == null) {
308                        b.push(Kind.Object, object);
309                    } else {
310                        Stamp piStamp = null;
311                        if (javaType.isArray()) {
312                            if (nonNull.asJavaConstant().asInt() != 0) {
313                                piStamp = StampFactory.exactNonNull(javaType);
314                            } else {
315                                piStamp = StampFactory.exact(javaType);
316                            }
317                        } else {
318                            piStamp = StampFactory.declaredTrusted(javaType, nonNull.asJavaConstant().asInt() != 0);
319                        }
320                        LogicNode compareNode = CompareNode.createCompareNode(object.graph(), Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), constantReflection);
321                        boolean skipAnchor = false;
322                        if (compareNode instanceof LogicConstantNode) {
323                            LogicConstantNode logicConstantNode = (LogicConstantNode) compareNode;
324                            if (logicConstantNode.getValue()) {
325                                skipAnchor = true;
326                            }
327                        }
328                        ConditionAnchorNode valueAnchorNode = null;
329                        if (!skipAnchor) {
330                            valueAnchorNode = b.add(new ConditionAnchorNode(compareNode));
331                        }
332                        b.addPush(Kind.Object, new PiNode(object, piStamp, valueAnchorNode));
333                    }
334                    return true;
335                } else if (canDelayIntrinsification) {
336                    return false;
337                } else {
338                    throw b.bailout("unsafeCast arguments could not reduce to a constant: " + clazz + ", " + nonNull);
339                }
340            }
341        });
342    }
343
344    protected static void registerUnsafeLoadStorePlugins(Registration r, Kind... kinds) {
345        for (Kind kind : kinds) {
346            String kindName = kind.getJavaName();
347            kindName = toUpperCase(kindName.charAt(0)) + kindName.substring(1);
348            String getName = "unsafeGet" + kindName;
349            String putName = "unsafePut" + kindName;
350            r.register4(getName, Object.class, long.class, boolean.class, Object.class, new CustomizedUnsafeLoadPlugin(kind));
351            r.register4(putName, Object.class, long.class, kind == Kind.Object ? Object.class : kind.toJavaClass(), Object.class, new CustomizedUnsafeStorePlugin(kind));
352        }
353    }
354
355    static class CustomizedUnsafeLoadPlugin implements InvocationPlugin {
356
357        private final Kind returnKind;
358
359        public CustomizedUnsafeLoadPlugin(Kind returnKind) {
360            this.returnKind = returnKind;
361        }
362
363        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode condition, ValueNode location) {
364            if (location.isConstant()) {
365                LocationIdentity locationIdentity;
366                if (location.isNullConstant()) {
367                    locationIdentity = LocationIdentity.any();
368                } else {
369                    locationIdentity = ObjectLocationIdentity.create(location.asJavaConstant());
370                }
371                LogicNode compare = b.add(CompareNode.createCompareNode(Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), b.getConstantReflection()));
372                b.addPush(returnKind, b.add(new UnsafeLoadNode(object, offset, returnKind, locationIdentity, compare)));
373                return true;
374            }
375            // TODO: should we throw b.bailout() here?
376            return false;
377        }
378    }
379
380    static class CustomizedUnsafeStorePlugin implements InvocationPlugin {
381
382        private final Kind kind;
383
384        public CustomizedUnsafeStorePlugin(Kind kind) {
385            this.kind = kind;
386        }
387
388        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode value, ValueNode location) {
389            ValueNode locationArgument = location;
390            if (locationArgument.isConstant()) {
391                LocationIdentity locationIdentity;
392                if (locationArgument.isNullConstant()) {
393                    locationIdentity = LocationIdentity.any();
394                } else {
395                    locationIdentity = ObjectLocationIdentity.create(locationArgument.asJavaConstant());
396                }
397
398                b.add(new UnsafeStoreNode(object, offset, value, kind, locationIdentity, null));
399                return true;
400            }
401            // TODO: should we throw b.bailout() here?
402            return false;
403        }
404    }
405}