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 jdk.internal.jvmci.code.MemoryBarriers.*;
026
027import java.lang.reflect.*;
028import java.util.*;
029
030import jdk.internal.jvmci.code.*;
031import jdk.internal.jvmci.common.*;
032import jdk.internal.jvmci.meta.*;
033import jdk.internal.jvmci.options.*;
034import sun.misc.*;
035
036import com.oracle.graal.api.directives.*;
037import com.oracle.graal.compiler.common.calc.*;
038import com.oracle.graal.compiler.common.type.*;
039import com.oracle.graal.graph.*;
040import com.oracle.graal.graphbuilderconf.*;
041import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver;
042import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
043import com.oracle.graal.nodes.*;
044import com.oracle.graal.nodes.calc.*;
045import com.oracle.graal.nodes.debug.*;
046import com.oracle.graal.nodes.extended.*;
047import com.oracle.graal.nodes.java.*;
048import com.oracle.graal.nodes.util.*;
049import com.oracle.graal.nodes.virtual.*;
050import com.oracle.graal.replacements.nodes.*;
051import com.oracle.graal.replacements.nodes.arithmetic.*;
052
053/**
054 * Provides non-runtime specific {@link InvocationPlugin}s.
055 */
056public class StandardGraphBuilderPlugins {
057
058    // @formatter:off
059    static class Options {
060        @Option(help = "Enable use of intrinsics for the JMH Blackhole class")
061        public static final OptionValue<Boolean> UseBlackholeSubstitution = new OptionValue<>(true);
062    }
063    // @formatter:on
064
065    public static void registerInvocationPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins, boolean useBoxingPlugins) {
066        registerObjectPlugins(plugins);
067        registerClassPlugins(plugins);
068        registerMathPlugins(plugins);
069        registerUnsignedMathPlugins(plugins);
070        registerCharacterPlugins(plugins);
071        registerShortPlugins(plugins);
072        registerIntegerLongPlugins(plugins, Kind.Int);
073        registerIntegerLongPlugins(plugins, Kind.Long);
074        registerFloatPlugins(plugins);
075        registerDoublePlugins(plugins);
076        registerStringPlugins(plugins);
077        registerArraysPlugins(plugins);
078        registerArrayPlugins(plugins);
079        registerUnsafePlugins(plugins);
080        registerEdgesPlugins(metaAccess, plugins);
081        registerGraalDirectivesPlugins(plugins);
082        if (useBoxingPlugins) {
083            registerBoxingPlugins(plugins);
084        }
085        if (Options.UseBlackholeSubstitution.getValue()) {
086            registerJMHBlackholePlugins(plugins);
087        }
088        registerJFRThrowablePlugins(plugins);
089    }
090
091    private static final Field STRING_VALUE_FIELD;
092
093    static {
094        try {
095            STRING_VALUE_FIELD = String.class.getDeclaredField("value");
096        } catch (NoSuchFieldException e) {
097            throw new JVMCIError(e);
098        }
099    }
100
101    private static void registerStringPlugins(InvocationPlugins plugins) {
102        Registration r = new Registration(plugins, String.class);
103        r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class);
104
105        r = new Registration(plugins, StringSubstitutions.class);
106        r.register1("getValue", String.class, new InvocationPlugin() {
107            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
108                ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
109                b.addPush(Kind.Object, new LoadFieldNode(value, field));
110                return true;
111            }
112        });
113    }
114
115    private static void registerArraysPlugins(InvocationPlugins plugins) {
116        Registration r = new Registration(plugins, Arrays.class);
117        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", boolean[].class, boolean[].class);
118        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", byte[].class, byte[].class);
119        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", short[].class, short[].class);
120        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class);
121        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", int[].class, int[].class);
122        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class);
123        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", long[].class, long[].class);
124        r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class);
125    }
126
127    private static void registerArrayPlugins(InvocationPlugins plugins) {
128        Registration r = new Registration(plugins, Array.class);
129        r.register2("newInstance", Class.class, int.class, new InvocationPlugin() {
130            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode componentType, ValueNode length) {
131                b.addPush(Kind.Object, new DynamicNewArrayNode(componentType, length, true));
132                return true;
133            }
134        });
135        r.registerMethodSubstitution(ArraySubstitutions.class, "getLength", Object.class);
136    }
137
138    private static void registerUnsafePlugins(InvocationPlugins plugins) {
139        Registration r = new Registration(plugins, Unsafe.class);
140        for (Kind kind : Kind.values()) {
141            if ((kind.isPrimitive() && kind != Kind.Void) || kind == Kind.Object) {
142                Class<?> javaClass = kind == Kind.Object ? Object.class : kind.toJavaClass();
143                String kindName = kind.name();
144                String getName = "get" + kindName;
145                String putName = "put" + kindName;
146                // Object-based accesses
147                r.register3(getName, Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, false));
148                r.register4(putName, Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, false));
149                // Volatile object-based accesses
150                r.register3(getName + "Volatile", Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, true));
151                r.register4(putName + "Volatile", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, true));
152                // Ordered object-based accesses
153                if (kind == Kind.Int || kind == Kind.Long || kind == Kind.Object) {
154                    r.register4("putOrdered" + kindName, Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, true));
155                }
156                if (kind != Kind.Boolean && kind != Kind.Object) {
157                    // Raw accesses to memory addresses
158                    r.register2(getName, Receiver.class, long.class, new UnsafeGetPlugin(kind, false));
159                    r.register3(putName, Receiver.class, long.class, kind.toJavaClass(), new UnsafePutPlugin(kind, false));
160                }
161            }
162        }
163
164        // Accesses to native memory addresses.
165        r.register2("getAddress", Receiver.class, long.class, new UnsafeGetPlugin(Kind.Long, false));
166        r.register3("putAddress", Receiver.class, long.class, long.class, new UnsafePutPlugin(Kind.Long, false));
167
168        for (Kind kind : new Kind[]{Kind.Int, Kind.Long, Kind.Object}) {
169            Class<?> javaClass = kind == Kind.Object ? Object.class : kind.toJavaClass();
170            r.register5("compareAndSwap" + kind.name(), Receiver.class, Object.class, long.class, javaClass, javaClass, new InvocationPlugin() {
171                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode expected, ValueNode x) {
172                    // Emits a null-check for the otherwise unused receiver
173                    unsafe.get();
174                    b.addPush(Kind.Int, new CompareAndSwapNode(object, offset, expected, x, kind, LocationIdentity.any()));
175                    b.getGraph().markUnsafeAccess();
176                    return true;
177                }
178            });
179        }
180
181        r.register2("allocateInstance", Receiver.class, Class.class, new InvocationPlugin() {
182            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode clazz) {
183                // Emits a null-check for the otherwise unused receiver
184                unsafe.get();
185                b.addPush(Kind.Object, new DynamicNewInstanceNode(clazz, true));
186                return true;
187            }
188        });
189    }
190
191    private static void registerIntegerLongPlugins(InvocationPlugins plugins, Kind kind) {
192        Class<?> declaringClass = kind.toBoxedJavaClass();
193        Class<?> type = kind.toJavaClass();
194        Registration r = new Registration(plugins, declaringClass);
195        r.register1("reverseBytes", type, new InvocationPlugin() {
196            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
197                b.push(kind, b.recursiveAppend(new ReverseBytesNode(value).canonical(null, value)));
198                return true;
199            }
200        });
201        r.register1("bitCount", type, new InvocationPlugin() {
202            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
203                b.push(Kind.Int, b.recursiveAppend(new BitCountNode(value).canonical(null, value)));
204                return true;
205            }
206        });
207        r.register2("divideUnsigned", type, type, new InvocationPlugin() {
208            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
209                b.push(kind, b.recursiveAppend(new UnsignedDivNode(dividend, divisor).canonical(null, dividend, divisor)));
210                return true;
211            }
212        });
213        r.register2("remainderUnsigned", type, type, new InvocationPlugin() {
214            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
215                b.push(kind, b.recursiveAppend(new UnsignedRemNode(dividend, divisor).canonical(null, dividend, divisor)));
216                return true;
217            }
218        });
219    }
220
221    private static void registerCharacterPlugins(InvocationPlugins plugins) {
222        Registration r = new Registration(plugins, Character.class);
223        r.register1("reverseBytes", char.class, new InvocationPlugin() {
224            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
225                // return (char) (Integer.reverse(i) >> 16);
226                ReverseBytesNode reverse = b.add(new ReverseBytesNode(value));
227                RightShiftNode rightShift = b.add(new RightShiftNode(reverse, b.add(ConstantNode.forInt(16))));
228                ZeroExtendNode charCast = b.add(new ZeroExtendNode(b.add(new NarrowNode(rightShift, 16)), 32));
229                b.push(Kind.Char, b.recursiveAppend(charCast.canonical(null, value)));
230                return true;
231            }
232        });
233    }
234
235    private static void registerShortPlugins(InvocationPlugins plugins) {
236        Registration r = new Registration(plugins, Short.class);
237        r.register1("reverseBytes", short.class, new InvocationPlugin() {
238            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
239                // return (short) (Integer.reverse(i) >> 16);
240                ReverseBytesNode reverse = b.add(new ReverseBytesNode(value));
241                RightShiftNode rightShift = b.add(new RightShiftNode(reverse, b.add(ConstantNode.forInt(16))));
242                SignExtendNode charCast = b.add(new SignExtendNode(b.add(new NarrowNode(rightShift, 16)), 32));
243                b.push(Kind.Short, b.recursiveAppend(charCast.canonical(null, value)));
244                return true;
245            }
246        });
247    }
248
249    private static void registerFloatPlugins(InvocationPlugins plugins) {
250        Registration r = new Registration(plugins, Float.class);
251        r.register1("floatToRawIntBits", float.class, new InvocationPlugin() {
252            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
253                b.push(Kind.Int, b.recursiveAppend(new ReinterpretNode(Kind.Int, value).canonical(null, value)));
254                return true;
255            }
256        });
257        r.register1("intBitsToFloat", int.class, new InvocationPlugin() {
258            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
259                b.push(Kind.Float, b.recursiveAppend(new ReinterpretNode(Kind.Float, value).canonical(null, value)));
260                return true;
261            }
262        });
263    }
264
265    private static void registerDoublePlugins(InvocationPlugins plugins) {
266        Registration r = new Registration(plugins, Double.class);
267        r.register1("doubleToRawLongBits", double.class, new InvocationPlugin() {
268            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
269                b.push(Kind.Long, b.recursiveAppend(new ReinterpretNode(Kind.Long, value).canonical(null, value)));
270                return true;
271            }
272        });
273        r.register1("longBitsToDouble", long.class, new InvocationPlugin() {
274            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
275                b.push(Kind.Double, b.recursiveAppend(new ReinterpretNode(Kind.Double, value).canonical(null, value)));
276                return true;
277            }
278        });
279    }
280
281    private static void registerMathPlugins(InvocationPlugins plugins) {
282        Registration r = new Registration(plugins, Math.class);
283        for (Kind kind : new Kind[]{Kind.Int, Kind.Long}) {
284            Class<?> type = kind.toJavaClass();
285            r.register2("addExact", type, type, new InvocationPlugin() {
286                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
287                    b.addPush(kind, new IntegerAddExactNode(x, y));
288                    return true;
289                }
290            });
291            r.register2("subtractExact", type, type, new InvocationPlugin() {
292                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
293                    b.addPush(kind, new IntegerSubExactNode(x, y));
294                    return true;
295                }
296            });
297            r.register2("multiplyExact", type, type, new InvocationPlugin() {
298                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
299                    b.addPush(kind, new IntegerMulExactNode(x, y));
300                    return true;
301                }
302            });
303        }
304        r.register1("abs", Float.TYPE, new InvocationPlugin() {
305            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
306                b.push(Kind.Float, b.recursiveAppend(new AbsNode(value).canonical(null, value)));
307                return true;
308            }
309        });
310        r.register1("abs", Double.TYPE, new InvocationPlugin() {
311            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
312                b.push(Kind.Double, b.recursiveAppend(new AbsNode(value).canonical(null, value)));
313                return true;
314            }
315        });
316        r.register1("sqrt", Double.TYPE, new InvocationPlugin() {
317            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
318                b.push(Kind.Double, b.recursiveAppend(new SqrtNode(value).canonical(null, value)));
319                return true;
320            }
321        });
322    }
323
324    public static class UnsignedMathPlugin implements InvocationPlugin {
325        private final Condition condition;
326
327        public UnsignedMathPlugin(Condition condition) {
328            this.condition = condition;
329        }
330
331        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
332            // the mirroring and negation operations get the condition into canonical form
333            boolean mirror = condition.canonicalMirror();
334            boolean negate = condition.canonicalNegate();
335            StructuredGraph graph = b.getGraph();
336
337            ValueNode lhs = mirror ? y : x;
338            ValueNode rhs = mirror ? x : y;
339
340            ValueNode trueValue = ConstantNode.forBoolean(!negate, graph);
341            ValueNode falseValue = ConstantNode.forBoolean(negate, graph);
342
343            Condition cond = mirror ? condition.mirror() : condition;
344            if (negate) {
345                cond = cond.negate();
346            }
347
348            LogicNode compare = CompareNode.createCompareNode(graph, cond, lhs, rhs, b.getConstantReflection());
349            b.addPush(Kind.Boolean, new ConditionalNode(compare, trueValue, falseValue));
350            return true;
351        }
352    }
353
354    private static void registerUnsignedMathPlugins(InvocationPlugins plugins) {
355        Registration r = new Registration(plugins, UnsignedMath.class);
356        r.register2("aboveThan", int.class, int.class, new UnsignedMathPlugin(Condition.AT));
357        r.register2("aboveThan", long.class, long.class, new UnsignedMathPlugin(Condition.AT));
358        r.register2("belowThan", int.class, int.class, new UnsignedMathPlugin(Condition.BT));
359        r.register2("belowThan", long.class, long.class, new UnsignedMathPlugin(Condition.BT));
360        r.register2("aboveOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.AE));
361        r.register2("aboveOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.AE));
362        r.register2("belowOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.BE));
363        r.register2("belowOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.BE));
364        r.register2("divide", int.class, int.class, new InvocationPlugin() {
365            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
366                b.push(Kind.Int, b.recursiveAppend(new UnsignedDivNode(x, y).canonical(null, x, y)));
367                return true;
368            }
369        });
370        r.register2("divide", long.class, long.class, new InvocationPlugin() {
371            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
372                b.push(Kind.Long, b.recursiveAppend(new UnsignedDivNode(x, y).canonical(null, x, y)));
373                return true;
374            }
375        });
376        r.register2("remainder", int.class, int.class, new InvocationPlugin() {
377            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
378                b.push(Kind.Int, b.recursiveAppend(new UnsignedRemNode(x, y).canonical(null, x, y)));
379                return true;
380            }
381        });
382        r.register2("remainder", long.class, long.class, new InvocationPlugin() {
383            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
384                b.push(Kind.Long, b.recursiveAppend(new UnsignedRemNode(x, y).canonical(null, x, y)));
385                return true;
386            }
387        });
388    }
389
390    protected static void registerBoxingPlugins(InvocationPlugins plugins) {
391        for (Kind kind : Kind.values()) {
392            if (kind.isPrimitive() && kind != Kind.Void) {
393                new BoxPlugin(kind).register(plugins);
394                new UnboxPlugin(kind).register(plugins);
395            }
396        }
397    }
398
399    private static void registerObjectPlugins(InvocationPlugins plugins) {
400        Registration r = new Registration(plugins, Object.class);
401        r.register1("<init>", Receiver.class, new InvocationPlugin() {
402            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
403                ValueNode object = receiver.get();
404                if (RegisterFinalizerNode.mayHaveFinalizer(object, b.getAssumptions())) {
405                    b.add(new RegisterFinalizerNode(object));
406                }
407                return true;
408            }
409        });
410        r.register1("getClass", Receiver.class, new InvocationPlugin() {
411            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
412                ValueNode object = receiver.get();
413                ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), GraphUtil.originalValue(object));
414                if (folded != null) {
415                    b.addPush(Kind.Object, folded);
416                } else {
417                    Stamp stamp = StampFactory.declaredNonNull(b.getMetaAccess().lookupJavaType(Class.class));
418                    b.addPush(Kind.Object, new GetClassNode(stamp, object));
419                }
420                return true;
421            }
422        });
423    }
424
425    private static void registerClassPlugins(InvocationPlugins plugins) {
426        Registration r = new Registration(plugins, Class.class);
427        r.register2("isInstance", Receiver.class, Object.class, new InvocationPlugin() {
428            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode object) {
429                LogicNode condition = b.add(InstanceOfDynamicNode.create(b.getConstantReflection(), type.get(), object));
430                b.push(Kind.Boolean, b.recursiveAppend(new ConditionalNode(condition).canonical(null)));
431                return true;
432            }
433        });
434        r.register2("isAssignableFrom", Receiver.class, Class.class, new InvocationPlugin() {
435            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode otherType) {
436                ClassIsAssignableFromNode condition = b.recursiveAppend(new ClassIsAssignableFromNode(type.get(), otherType));
437                b.push(Kind.Boolean, b.recursiveAppend(new ConditionalNode(condition).canonical(null)));
438                return true;
439            }
440        });
441    }
442
443    /**
444     * Substitutions for improving the performance of some critical methods in {@link Edges}. These
445     * substitutions improve the performance by forcing the relevant methods to be inlined
446     * (intrinsification being a special form of inlining) and removing a checked cast. The latter
447     * cannot be done directly in Java code as {@link DeferredPiNode} is not available to the
448     * project containing {@link Edges}.
449     */
450    private static void registerEdgesPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) {
451        Registration r = new Registration(plugins, Edges.class);
452        for (Class<?> c : new Class<?>[]{Node.class, NodeList.class}) {
453            r.register2("get" + c.getSimpleName() + "Unsafe", Node.class, long.class, new InvocationPlugin() {
454                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset) {
455                    ValueNode value = b.add(new UnsafeLoadNode(node, offset, Kind.Object, LocationIdentity.any()));
456                    boolean exactType = false;
457                    boolean nonNull = false;
458                    b.addPush(Kind.Object, new PiNode(value, metaAccess.lookupJavaType(c), exactType, nonNull));
459                    return true;
460                }
461            });
462            r.register3("put" + c.getSimpleName() + "Unsafe", Node.class, long.class, c, new InvocationPlugin() {
463                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset, ValueNode value) {
464                    b.add(new UnsafeStoreNode(node, offset, value, Kind.Object, LocationIdentity.any()));
465                    return true;
466                }
467            });
468        }
469    }
470
471    public static class BoxPlugin implements InvocationPlugin {
472
473        private final Kind kind;
474
475        BoxPlugin(Kind kind) {
476            this.kind = kind;
477        }
478
479        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
480            if (b.parsingIntrinsic()) {
481                ResolvedJavaMethod rootMethod = b.getGraph().method();
482                if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
483                    // Disable invocation plugins for boxing snippets so that the
484                    // original JDK methods are inlined
485                    return false;
486                }
487            }
488            ResolvedJavaType resultType = b.getMetaAccess().lookupJavaType(kind.toBoxedJavaClass());
489            b.addPush(Kind.Object, new BoxNode(value, resultType, kind));
490            return true;
491        }
492
493        void register(InvocationPlugins plugins) {
494            plugins.register(this, kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass());
495        }
496    }
497
498    public static class UnboxPlugin implements InvocationPlugin {
499
500        private final Kind kind;
501
502        UnboxPlugin(Kind kind) {
503            this.kind = kind;
504        }
505
506        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
507            if (b.parsingIntrinsic()) {
508                ResolvedJavaMethod rootMethod = b.getGraph().method();
509                if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
510                    // Disable invocation plugins for unboxing snippets so that the
511                    // original JDK methods are inlined
512                    return false;
513                }
514            }
515            ValueNode valueNode = UnboxNode.create(b.getMetaAccess(), b.getConstantReflection(), receiver.get(), kind);
516            b.addPush(kind, valueNode);
517            return true;
518        }
519
520        void register(InvocationPlugins plugins) {
521            String name = kind.toJavaClass().getSimpleName() + "Value";
522            plugins.register(this, kind.toBoxedJavaClass(), name, Receiver.class);
523        }
524    }
525
526    public static class UnsafeGetPlugin implements InvocationPlugin {
527
528        private final Kind returnKind;
529        private final boolean isVolatile;
530
531        public UnsafeGetPlugin(Kind returnKind, boolean isVolatile) {
532            this.returnKind = returnKind;
533            this.isVolatile = isVolatile;
534        }
535
536        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address) {
537            // Emits a null-check for the otherwise unused receiver
538            unsafe.get();
539            b.addPush(returnKind, new DirectReadNode(address, returnKind));
540            b.getGraph().markUnsafeAccess();
541            return true;
542        }
543
544        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset) {
545            // Emits a null-check for the otherwise unused receiver
546            unsafe.get();
547            if (isVolatile) {
548                b.add(new MembarNode(JMM_PRE_VOLATILE_READ));
549            }
550            b.addPush(returnKind, new UnsafeLoadNode(object, offset, returnKind, LocationIdentity.any()));
551            if (isVolatile) {
552                b.add(new MembarNode(JMM_POST_VOLATILE_READ));
553            }
554            b.getGraph().markUnsafeAccess();
555            return true;
556        }
557    }
558
559    public static class UnsafePutPlugin implements InvocationPlugin {
560
561        private final Kind kind;
562        private final boolean isVolatile;
563
564        public UnsafePutPlugin(Kind kind, boolean isVolatile) {
565            this.kind = kind;
566            this.isVolatile = isVolatile;
567        }
568
569        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address, ValueNode value) {
570            // Emits a null-check for the otherwise unused receiver
571            unsafe.get();
572            b.add(new DirectStoreNode(address, value, kind));
573            b.getGraph().markUnsafeAccess();
574            return true;
575        }
576
577        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode value) {
578            // Emits a null-check for the otherwise unused receiver
579            unsafe.get();
580            if (isVolatile) {
581                b.add(new MembarNode(JMM_PRE_VOLATILE_WRITE));
582            }
583            b.add(new UnsafeStoreNode(object, offset, value, kind, LocationIdentity.any()));
584            if (isVolatile) {
585                b.add(new MembarNode(JMM_PRE_VOLATILE_WRITE));
586            }
587            b.getGraph().markUnsafeAccess();
588            return true;
589        }
590    }
591
592    private static void registerGraalDirectivesPlugins(InvocationPlugins plugins) {
593        Registration r = new Registration(plugins, GraalDirectives.class);
594        r.register0("deoptimize", new InvocationPlugin() {
595            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
596                b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
597                return true;
598            }
599        });
600
601        r.register0("deoptimizeAndInvalidate", new InvocationPlugin() {
602            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
603                b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
604                return true;
605            }
606        });
607
608        r.register0("inCompiledCode", new InvocationPlugin() {
609            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
610                b.addPush(Kind.Boolean, ConstantNode.forBoolean(true));
611                return true;
612            }
613        });
614
615        r.register0("controlFlowAnchor", new InvocationPlugin() {
616            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
617                b.add(new ControlFlowAnchorNode());
618                return true;
619            }
620        });
621
622        r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
623            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
624                b.addPush(Kind.Boolean, new BranchProbabilityNode(probability, condition));
625                return true;
626            }
627        });
628
629        InvocationPlugin blackholePlugin = new InvocationPlugin() {
630            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
631                b.add(new BlackholeNode(value));
632                return true;
633            }
634        };
635
636        for (Kind kind : Kind.values()) {
637            if ((kind.isPrimitive() && kind != Kind.Void) || kind == Kind.Object) {
638                Class<?> javaClass = kind == Kind.Object ? Object.class : kind.toJavaClass();
639                r.register1("blackhole", javaClass, blackholePlugin);
640
641                r.register1("opaque", javaClass, new InvocationPlugin() {
642                    public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
643                        b.addPush(kind, new OpaqueNode(value));
644                        return true;
645                    }
646                });
647            }
648        }
649
650        r.register1("guardingNonNull", Object.class, new InvocationPlugin() {
651            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
652                ObjectStamp objectStamp = (ObjectStamp) value.stamp();
653                if (objectStamp.nonNull()) {
654                    b.addPush(value.getKind(), value);
655                    return true;
656                } else if (objectStamp.alwaysNull()) {
657                    b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.NullCheckException));
658                    return true;
659                }
660                IsNullNode isNull = b.add(new IsNullNode(value));
661                FixedGuardNode fixedGuard = b.add(new FixedGuardNode(isNull, DeoptimizationReason.NullCheckException, DeoptimizationAction.None, true));
662                Stamp newStamp = objectStamp.improveWith(StampFactory.objectNonNull());
663                b.addPush(value.getKind(), new PiNode(value, newStamp, fixedGuard));
664                return true;
665            }
666        });
667
668        r.register1("ensureVirtualized", Object.class, new InvocationPlugin() {
669            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
670                b.add(new EnsureVirtualizedNode(object, false));
671                return true;
672            }
673        });
674        r.register1("ensureVirtualizedHere", Object.class, new InvocationPlugin() {
675            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
676                b.add(new EnsureVirtualizedNode(object, true));
677                return true;
678            }
679        });
680    }
681
682    private static void registerJMHBlackholePlugins(InvocationPlugins plugins) {
683        InvocationPlugin blackholePlugin = new InvocationPlugin() {
684            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver blackhole, ValueNode value) {
685                blackhole.get();
686                b.add(new BlackholeNode(value));
687                return true;
688            }
689        };
690        String[] names = {"org.openjdk.jmh.infra.Blackhole", "org.openjdk.jmh.logic.BlackHole"};
691        for (String name : names) {
692            Class<?> blackholeClass;
693            blackholeClass = MethodSubstitutionPlugin.resolveClass(name, true);
694            if (blackholeClass != null) {
695                Registration r = new Registration(plugins, blackholeClass);
696                for (Kind kind : Kind.values()) {
697                    if ((kind.isPrimitive() && kind != Kind.Void) || kind == Kind.Object) {
698                        Class<?> javaClass = kind == Kind.Object ? Object.class : kind.toJavaClass();
699                        r.register2("consume", Receiver.class, javaClass, blackholePlugin);
700                    }
701                }
702                r.register2("consume", Receiver.class, Object[].class, blackholePlugin);
703            }
704        }
705    }
706
707    private static void registerJFRThrowablePlugins(InvocationPlugins plugins) {
708        String name = "oracle.jrockit.jfr.jdkevents.ThrowableTracer";
709        Class<?> tracerClass = MethodSubstitutionPlugin.resolveClass(name, true);
710        if (tracerClass != null) {
711            Registration r = new Registration(plugins, tracerClass);
712            r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
713                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
714                    b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnType(), throwable, message));
715                    return true;
716                }
717            });
718        }
719    }
720}