1/*
2 * Copyright (c) 2015, 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 */
23package org.graalvm.compiler.lir.jtt;
24
25import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
26import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
27import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
28
29import java.lang.annotation.ElementType;
30import java.lang.annotation.RetentionPolicy;
31import java.lang.reflect.Method;
32import java.lang.reflect.Modifier;
33import java.util.stream.Stream;
34
35import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
36import org.graalvm.compiler.core.common.type.StampFactory;
37import org.graalvm.compiler.graph.NodeClass;
38import org.graalvm.compiler.graph.NodeInputList;
39import org.graalvm.compiler.jtt.JTTTest;
40import org.graalvm.compiler.nodeinfo.NodeInfo;
41import org.graalvm.compiler.nodes.FixedWithNextNode;
42import org.graalvm.compiler.nodes.ValueNode;
43import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
44import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
45import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
46import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
47import org.graalvm.compiler.nodes.spi.LIRLowerable;
48import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
49
50import jdk.vm.ci.meta.JavaKind;
51import jdk.vm.ci.meta.ResolvedJavaMethod;
52import jdk.vm.ci.meta.Value;
53
54/**
55 * Base class for LIR tests.
56 * <p>
57 * It provides facilities to replace methods with {@link LIRTestSpecification arbitrary LIR
58 * instructions}.
59 */
60public abstract class LIRTest extends JTTTest {
61
62    @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
63    private static final class LIRTestNode extends FixedWithNextNode implements LIRLowerable {
64
65        public static final NodeClass<LIRTestNode> TYPE = NodeClass.create(LIRTestNode.class);
66        @Input protected ValueNode opsNode;
67        @Input protected NodeInputList<ValueNode> values;
68        public final SnippetReflectionProvider snippetReflection;
69
70        protected LIRTestNode(SnippetReflectionProvider snippetReflection, JavaKind kind, ValueNode opsNode, ValueNode[] values) {
71            super(TYPE, StampFactory.forKind(kind));
72            this.opsNode = opsNode;
73            this.values = new NodeInputList<>(this, values);
74            this.snippetReflection = snippetReflection;
75        }
76
77        public NodeInputList<ValueNode> values() {
78            return values;
79        }
80
81        public ValueNode getLIROpsNode() {
82            return opsNode;
83        }
84
85        @Override
86        public void generate(NodeLIRBuilderTool gen) {
87            LIRTestSpecification ops = getLIROperations();
88            Stream<Value> v = values().stream().map(node -> gen.operand(node));
89
90            ops.generate(gen.getLIRGeneratorTool(), v.toArray(size -> new Value[size]));
91            Value result = ops.getResult();
92            if (result != null) {
93                gen.setResult(this, result);
94            }
95        }
96
97        public LIRTestSpecification getLIROperations() {
98            assert getLIROpsNode().isConstant();
99            LIRTestSpecification spec = snippetReflection.asObject(LIRTestSpecification.class, getLIROpsNode().asJavaConstant());
100            return spec;
101        }
102    }
103
104    @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
105    private static final class LIRValueNode extends FixedWithNextNode implements LIRLowerable {
106
107        public static final NodeClass<LIRValueNode> TYPE = NodeClass.create(LIRValueNode.class);
108        @Input protected ValueNode opsNode;
109        @Input protected ValueNode name;
110        public final SnippetReflectionProvider snippetReflection;
111
112        protected LIRValueNode(SnippetReflectionProvider snippetReflection, JavaKind kind, ValueNode opsNode, ValueNode name) {
113            super(TYPE, StampFactory.forKind(kind));
114            this.opsNode = opsNode;
115            this.name = name;
116            this.snippetReflection = snippetReflection;
117        }
118
119        public ValueNode getLIROpsNode() {
120            return opsNode;
121        }
122
123        @Override
124        public void generate(NodeLIRBuilderTool gen) {
125            LIRTestSpecification spec = getLIROperations();
126            Value output = spec.getOutput(getName());
127            gen.setResult(this, isVariable(output) ? output : gen.getLIRGeneratorTool().emitMove(output));
128        }
129
130        private String getName() {
131            assert name.isConstant();
132            return snippetReflection.asObject(String.class, name.asJavaConstant());
133        }
134
135        private LIRTestSpecification getLIROperations() {
136            assert getLIROpsNode().isConstant();
137            return snippetReflection.asObject(LIRTestSpecification.class, getLIROpsNode().asJavaConstant());
138        }
139
140    }
141
142    private InvocationPlugin lirTestPlugin = new InvocationPlugin() {
143        @Override
144        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec) {
145            JavaKind returnKind = targetMethod.getSignature().getReturnKind();
146            LIRTestNode node = new LIRTestNode(getSnippetReflection(), returnKind, spec, new ValueNode[]{});
147            addNode(b, returnKind, node);
148            return true;
149        }
150
151        @Override
152        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0) {
153            JavaKind returnKind = targetMethod.getSignature().getReturnKind();
154            LIRTestNode node = new LIRTestNode(getSnippetReflection(), returnKind, spec, new ValueNode[]{arg0});
155            addNode(b, returnKind, node);
156            return true;
157        }
158
159        @Override
160        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1) {
161            JavaKind returnKind = targetMethod.getSignature().getReturnKind();
162            LIRTestNode node = new LIRTestNode(getSnippetReflection(), returnKind, spec, new ValueNode[]{arg0, arg1});
163            addNode(b, returnKind, node);
164            return true;
165        }
166
167        @Override
168        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2) {
169            JavaKind returnKind = targetMethod.getSignature().getReturnKind();
170            LIRTestNode node = new LIRTestNode(getSnippetReflection(), returnKind, spec, new ValueNode[]{arg0, arg1, arg2});
171            addNode(b, returnKind, node);
172            return true;
173        }
174
175        @Override
176        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
177            JavaKind returnKind = targetMethod.getSignature().getReturnKind();
178            LIRTestNode node = new LIRTestNode(getSnippetReflection(), returnKind, spec, new ValueNode[]{arg0, arg1, arg2, arg3});
179            addNode(b, returnKind, node);
180            return true;
181        }
182
183        private void addNode(GraphBuilderContext b, JavaKind returnKind, LIRTestNode node) {
184            if (returnKind.equals(JavaKind.Void)) {
185                b.add(node);
186            } else {
187                b.addPush(returnKind, node);
188            }
189        }
190
191    };
192
193    @Override
194    protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) {
195        InvocationPlugins invocationPlugins = conf.getPlugins().getInvocationPlugins();
196
197        Class<? extends LIRTest> c = getClass();
198        for (Method m : c.getMethods()) {
199            if (m.getAnnotation(LIRIntrinsic.class) != null) {
200                assert Modifier.isStatic(m.getModifiers());
201                Class<?>[] p = m.getParameterTypes();
202                assert p.length > 0;
203                assert LIRTestSpecification.class.isAssignableFrom(p[0]);
204
205                invocationPlugins.register(lirTestPlugin, c, m.getName(), p);
206            }
207        }
208        InvocationPlugin outputPlugin = new InvocationPlugin() {
209            @Override
210            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode name, ValueNode expected) {
211                JavaKind returnKind = targetMethod.getSignature().getReturnKind();
212                b.addPush(returnKind, new LIRValueNode(getSnippetReflection(), returnKind, spec, name));
213                return true;
214            }
215        };
216        invocationPlugins.register(outputPlugin, LIRTest.class, "getOutput", new Class<?>[]{LIRTestSpecification.class, String.class, Object.class});
217        invocationPlugins.register(outputPlugin, LIRTest.class, "getOutput", new Class<?>[]{LIRTestSpecification.class, String.class, int.class});
218        return super.editGraphBuilderConfiguration(conf);
219    }
220
221    @SuppressWarnings("unused")
222    public static byte getOutput(LIRTestSpecification spec, String name, byte expected) {
223        return expected;
224    }
225
226    @SuppressWarnings("unused")
227    public static short getOutput(LIRTestSpecification spec, String name, short expected) {
228        return expected;
229    }
230
231    @SuppressWarnings("unused")
232    public static int getOutput(LIRTestSpecification spec, String name, int expected) {
233        return expected;
234    }
235
236    @SuppressWarnings("unused")
237    public static long getOutput(LIRTestSpecification spec, String name, long expected) {
238        return expected;
239    }
240
241    @SuppressWarnings("unused")
242    public static float getOutput(LIRTestSpecification spec, String name, float expected) {
243        return expected;
244    }
245
246    @SuppressWarnings("unused")
247    public static double getOutput(LIRTestSpecification spec, String name, double expected) {
248        return expected;
249    }
250
251    @SuppressWarnings("unused")
252    public static Object getOutput(LIRTestSpecification spec, String name, Object expected) {
253        return expected;
254    }
255
256    @java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
257    @java.lang.annotation.Target(ElementType.METHOD)
258    public static @interface LIRIntrinsic {
259    }
260
261}
262