43972
|
1 |
/*
|
58299
|
2 |
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
|
43972
|
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 |
*/
|
50858
|
23 |
|
|
24 |
|
43972
|
25 |
package org.graalvm.compiler.replacements.test;
|
|
26 |
|
54084
|
27 |
import static org.graalvm.compiler.debug.DebugOptions.DumpOnError;
|
46509
|
28 |
import static org.graalvm.compiler.java.BytecodeParserOptions.InlinePartialIntrinsicExitDuringParsing;
|
|
29 |
|
43972
|
30 |
import java.util.function.Function;
|
|
31 |
|
46509
|
32 |
import org.graalvm.compiler.api.directives.GraalDirectives;
|
43972
|
33 |
import org.graalvm.compiler.api.replacements.ClassSubstitution;
|
|
34 |
import org.graalvm.compiler.api.replacements.MethodSubstitution;
|
46807
|
35 |
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
|
43972
|
36 |
import org.graalvm.compiler.bytecode.BytecodeProvider;
|
46807
|
37 |
import org.graalvm.compiler.core.common.LIRKind;
|
|
38 |
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
|
|
39 |
import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
|
|
40 |
import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
|
|
41 |
import org.graalvm.compiler.core.common.type.Stamp;
|
|
42 |
import org.graalvm.compiler.core.common.type.StampFactory;
|
|
43 |
import org.graalvm.compiler.debug.DebugContext;
|
54084
|
44 |
import org.graalvm.compiler.debug.GraalError;
|
46509
|
45 |
import org.graalvm.compiler.graph.GraalGraphError;
|
43972
|
46 |
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
|
|
47 |
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
|
54084
|
48 |
import org.graalvm.compiler.java.BytecodeParser.BytecodeParserError;
|
46807
|
49 |
import org.graalvm.compiler.nodes.FrameState;
|
43972
|
50 |
import org.graalvm.compiler.nodes.PiNode;
|
46509
|
51 |
import org.graalvm.compiler.nodes.StructuredGraph;
|
54084
|
52 |
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
|
|
53 |
import org.graalvm.compiler.nodes.StructuredGraph.Builder;
|
46509
|
54 |
import org.graalvm.compiler.nodes.ValueNode;
|
54084
|
55 |
import org.graalvm.compiler.nodes.extended.ForeignCallNode;
|
52578
|
56 |
import org.graalvm.compiler.nodes.extended.OpaqueNode;
|
46509
|
57 |
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
|
|
58 |
import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
|
46459
|
59 |
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
|
43972
|
60 |
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
|
|
61 |
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
|
46807
|
62 |
import org.graalvm.compiler.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;
|
|
63 |
import org.graalvm.compiler.nodes.spi.LoweringTool;
|
46509
|
64 |
import org.graalvm.compiler.options.OptionValues;
|
46807
|
65 |
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
|
|
66 |
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
|
|
67 |
import org.graalvm.compiler.phases.common.FloatingReadPhase;
|
|
68 |
import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
|
|
69 |
import org.graalvm.compiler.phases.common.GuardLoweringPhase;
|
|
70 |
import org.graalvm.compiler.phases.common.LoweringPhase;
|
|
71 |
import org.graalvm.compiler.phases.tiers.HighTierContext;
|
55509
|
72 |
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
|
49873
|
73 |
import jdk.internal.vm.compiler.word.LocationIdentity;
|
46509
|
74 |
import org.junit.Assert;
|
54084
|
75 |
import org.junit.Assume;
|
46509
|
76 |
import org.junit.BeforeClass;
|
46459
|
77 |
import org.junit.Test;
|
43972
|
78 |
|
46509
|
79 |
import jdk.vm.ci.code.InstalledCode;
|
46807
|
80 |
import jdk.vm.ci.meta.JavaKind;
|
43972
|
81 |
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
|
82 |
|
|
83 |
/**
|
|
84 |
* Tests for expected behavior when parsing snippets and intrinsics.
|
|
85 |
*/
|
46344
|
86 |
public class ReplacementsParseTest extends ReplacementsTest {
|
43972
|
87 |
|
46509
|
88 |
private static final String IN_COMPILED_HANDLER_MARKER = "*** in compiled handler ***";
|
|
89 |
|
|
90 |
/**
|
|
91 |
* Marker value to indicate an exception handler was interpreted. We cannot use a complex string
|
|
92 |
* expression in this context without risking non-deterministic behavior dependent on whether
|
|
93 |
* String intrinsics are applied or whether String expression evaluation hit an uncommon trap
|
|
94 |
* when executed by C1 or C2 (and thus potentially altering the profile such that the exception
|
|
95 |
* handler is *not* compiled by Graal even when we want it to be).
|
|
96 |
*/
|
|
97 |
private static final String IN_INTERPRETED_HANDLER_MARKER = "*** in interpreted handler ***";
|
|
98 |
|
|
99 |
private InlineInvokePlugin.InlineInfo inlineInvokeDecision;
|
46536
|
100 |
private String inlineInvokeMethodName = null;
|
46509
|
101 |
|
|
102 |
@SuppressWarnings("serial")
|
|
103 |
static class CustomError extends Error {
|
|
104 |
CustomError(String message) {
|
|
105 |
super(message);
|
|
106 |
}
|
|
107 |
}
|
|
108 |
|
46459
|
109 |
static final Object THROW_EXCEPTION_MARKER = new Object() {
|
43972
|
110 |
@Override
|
|
111 |
public String toString() {
|
|
112 |
return "THROW_EXCEPTION_MARKER";
|
|
113 |
}
|
|
114 |
};
|
|
115 |
|
46509
|
116 |
static int copyFirstBody(byte[] left, byte[] right, boolean left2right) {
|
|
117 |
if (left2right) {
|
|
118 |
byte e = left[0];
|
|
119 |
right[0] = e;
|
|
120 |
return e;
|
|
121 |
} else {
|
|
122 |
byte e = right[0];
|
|
123 |
left[0] = e;
|
|
124 |
return e;
|
|
125 |
}
|
|
126 |
}
|
|
127 |
|
|
128 |
static int copyFirstL2RBody(byte[] left, byte[] right) {
|
|
129 |
byte e = left[0];
|
|
130 |
right[0] = e;
|
|
131 |
return e;
|
|
132 |
}
|
|
133 |
|
46459
|
134 |
static class TestObject {
|
43972
|
135 |
static double next(double v) {
|
|
136 |
return Math.nextAfter(v, 1.0);
|
|
137 |
}
|
|
138 |
|
|
139 |
static double next2(double v) {
|
|
140 |
return Math.nextAfter(v, 1.0);
|
|
141 |
}
|
|
142 |
|
|
143 |
static double nextAfter(double x, double d) {
|
|
144 |
return Math.nextAfter(x, d);
|
|
145 |
}
|
|
146 |
|
46459
|
147 |
TestObject() {
|
|
148 |
this(null);
|
|
149 |
}
|
|
150 |
|
|
151 |
TestObject(Object id) {
|
|
152 |
this.id = id;
|
|
153 |
}
|
|
154 |
|
|
155 |
final Object id;
|
|
156 |
|
|
157 |
String stringizeId() {
|
46509
|
158 |
Object res = id;
|
|
159 |
if (res == THROW_EXCEPTION_MARKER) {
|
46459
|
160 |
// Tests exception throwing from partial intrinsification
|
46509
|
161 |
throw new CustomError("ex");
|
46459
|
162 |
}
|
46509
|
163 |
return String.valueOf(res);
|
46459
|
164 |
}
|
|
165 |
|
43972
|
166 |
static String stringize(Object obj) {
|
46509
|
167 |
Object res = obj;
|
|
168 |
if (res == THROW_EXCEPTION_MARKER) {
|
43972
|
169 |
// Tests exception throwing from partial intrinsification
|
46509
|
170 |
throw new CustomError("ex");
|
43972
|
171 |
}
|
46509
|
172 |
return String.valueOf(res);
|
43972
|
173 |
}
|
|
174 |
|
|
175 |
static String identity(String s) {
|
|
176 |
return s;
|
|
177 |
}
|
46509
|
178 |
|
|
179 |
/**
|
|
180 |
* @see TestObjectSubstitutions#copyFirst(byte[], byte[], boolean)
|
|
181 |
*/
|
|
182 |
static int copyFirst(byte[] left, byte[] right, boolean left2right) {
|
|
183 |
return copyFirstBody(left, right, left2right);
|
|
184 |
}
|
|
185 |
|
|
186 |
/**
|
|
187 |
* @see TestObjectSubstitutions#copyFirstL2R(byte[], byte[])
|
|
188 |
*/
|
|
189 |
static int copyFirstL2R(byte[] left, byte[] right) {
|
|
190 |
return copyFirstL2RBody(left, right);
|
|
191 |
}
|
46807
|
192 |
|
|
193 |
static int nonVoidIntrinsicWithCall(@SuppressWarnings("unused") int x, int y) {
|
|
194 |
return y;
|
|
195 |
}
|
|
196 |
|
|
197 |
static int nonVoidIntrinsicWithOptimizedSplit(int x) {
|
|
198 |
return x;
|
|
199 |
}
|
54084
|
200 |
|
|
201 |
static int div(int x, int y) {
|
|
202 |
return x / y;
|
|
203 |
}
|
43972
|
204 |
}
|
|
205 |
|
46459
|
206 |
@ClassSubstitution(TestObject.class)
|
|
207 |
static class TestObjectSubstitutions {
|
43972
|
208 |
|
|
209 |
@MethodSubstitution(isStatic = true)
|
|
210 |
static double nextAfter(double x, double d) {
|
|
211 |
double xx = (x == -0.0 ? 0.0 : x);
|
|
212 |
return Math.nextAfter(xx, d);
|
|
213 |
}
|
|
214 |
|
|
215 |
/**
|
46459
|
216 |
* Tests conditional intrinsification of a static method.
|
43972
|
217 |
*/
|
|
218 |
@MethodSubstitution
|
|
219 |
static String stringize(Object obj) {
|
|
220 |
if (obj != null && obj.getClass() == String.class) {
|
|
221 |
return asNonNullString(obj);
|
|
222 |
} else {
|
|
223 |
// A recursive call denotes exiting/deoptimizing
|
|
224 |
// out of the partial intrinsification to the
|
|
225 |
// slow/uncommon case.
|
|
226 |
return stringize(obj);
|
|
227 |
}
|
|
228 |
}
|
|
229 |
|
46459
|
230 |
/**
|
|
231 |
* Tests conditional intrinsification of a non-static method.
|
|
232 |
*/
|
|
233 |
@MethodSubstitution(isStatic = false)
|
|
234 |
static String stringizeId(TestObject thisObj) {
|
|
235 |
if (thisObj.id != null && thisObj.id.getClass() == String.class) {
|
|
236 |
return asNonNullString(thisObj.id);
|
|
237 |
} else {
|
|
238 |
// A recursive call denotes exiting/deoptimizing
|
|
239 |
// out of the partial intrinsification to the
|
|
240 |
// slow/uncommon case.
|
|
241 |
return outOfLinePartialIntrinsification(thisObj);
|
|
242 |
}
|
|
243 |
}
|
|
244 |
|
|
245 |
static String outOfLinePartialIntrinsification(TestObject thisObj) {
|
|
246 |
return stringizeId(thisObj);
|
|
247 |
}
|
|
248 |
|
43972
|
249 |
public static String asNonNullString(Object object) {
|
|
250 |
return asNonNullStringIntrinsic(object, String.class, true, true);
|
|
251 |
}
|
|
252 |
|
|
253 |
@NodeIntrinsic(PiNode.class)
|
|
254 |
private static native String asNonNullStringIntrinsic(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull);
|
|
255 |
|
|
256 |
/**
|
46509
|
257 |
* An valid intrinsic as the frame state associated with the merge should prevent the frame
|
|
258 |
* states associated with the array stores from being associated with subsequent
|
|
259 |
* deoptimizing nodes.
|
|
260 |
*/
|
|
261 |
@MethodSubstitution
|
|
262 |
static int copyFirst(byte[] left, byte[] right, boolean left2right) {
|
|
263 |
return copyFirstBody(left, right, left2right);
|
|
264 |
}
|
|
265 |
|
|
266 |
/**
|
|
267 |
* An invalid intrinsic as the frame state associated with the array assignment can leak out
|
|
268 |
* to subsequent deoptimizing nodes.
|
|
269 |
*/
|
|
270 |
@MethodSubstitution
|
|
271 |
static int copyFirstL2R(byte[] left, byte[] right) {
|
|
272 |
return copyFirstL2RBody(left, right);
|
|
273 |
}
|
|
274 |
|
|
275 |
/**
|
43972
|
276 |
* Tests that non-capturing lambdas are folded away.
|
|
277 |
*/
|
|
278 |
@MethodSubstitution
|
|
279 |
static String identity(String value) {
|
|
280 |
return apply(s -> s, value);
|
|
281 |
}
|
|
282 |
|
|
283 |
private static String apply(Function<String, String> f, String value) {
|
|
284 |
return f.apply(value);
|
|
285 |
}
|
46807
|
286 |
|
|
287 |
@MethodSubstitution(isStatic = true)
|
|
288 |
static int nonVoidIntrinsicWithCall(int x, int y) {
|
|
289 |
nonVoidIntrinsicWithCallStub(x);
|
|
290 |
return y;
|
|
291 |
}
|
|
292 |
|
|
293 |
@MethodSubstitution(isStatic = true)
|
|
294 |
static int nonVoidIntrinsicWithOptimizedSplit(int x) {
|
|
295 |
if (x == GraalDirectives.opaque(x)) {
|
|
296 |
nonVoidIntrinsicWithCallStub(x);
|
|
297 |
}
|
|
298 |
return x;
|
|
299 |
}
|
|
300 |
|
54084
|
301 |
@MethodSubstitution
|
|
302 |
static int div(int x, int y) {
|
|
303 |
assert y != 0;
|
|
304 |
return x / y;
|
|
305 |
}
|
|
306 |
|
46807
|
307 |
public static void nonVoidIntrinsicWithCallStub(int zLen) {
|
|
308 |
nonVoidIntrinsicWithCallStub(STUB_CALL, zLen);
|
|
309 |
}
|
|
310 |
|
|
311 |
static final ForeignCallDescriptor STUB_CALL = new ForeignCallDescriptor("stubCall", void.class, int.class);
|
|
312 |
|
|
313 |
@NodeIntrinsic(ForeignCallNode.class)
|
|
314 |
private static native void nonVoidIntrinsicWithCallStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, int zLen);
|
|
315 |
|
43972
|
316 |
}
|
|
317 |
|
|
318 |
@Override
|
46459
|
319 |
protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
|
46344
|
320 |
BytecodeProvider replacementBytecodeProvider = getSystemClassLoaderBytecodeProvider();
|
46459
|
321 |
Registration r = new Registration(invocationPlugins, TestObject.class, replacementBytecodeProvider);
|
46807
|
322 |
NodeIntrinsicPluginFactory.InjectionProvider injections = new DummyInjectionProvider();
|
|
323 |
new PluginFactory_ReplacementsParseTest().registerPlugins(invocationPlugins, injections);
|
46459
|
324 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "nextAfter", double.class, double.class);
|
|
325 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringize", Object.class);
|
|
326 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringizeId", Receiver.class);
|
46509
|
327 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirst", byte[].class, byte[].class, boolean.class);
|
|
328 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirstL2R", byte[].class, byte[].class);
|
46807
|
329 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "nonVoidIntrinsicWithCall", int.class, int.class);
|
|
330 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "nonVoidIntrinsicWithOptimizedSplit", int.class);
|
54084
|
331 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "div", int.class, int.class);
|
46459
|
332 |
|
43972
|
333 |
if (replacementBytecodeProvider.supportsInvokedynamic()) {
|
46459
|
334 |
r.registerMethodSubstitution(TestObjectSubstitutions.class, "identity", String.class);
|
43972
|
335 |
}
|
46459
|
336 |
super.registerInvocationPlugins(invocationPlugins);
|
43972
|
337 |
}
|
|
338 |
|
46509
|
339 |
@BeforeClass
|
|
340 |
public static void warmupProfiles() {
|
|
341 |
for (int i = 0; i < 40000; i++) {
|
|
342 |
callCopyFirst(new byte[16], new byte[16], true);
|
|
343 |
callCopyFirstL2R(new byte[16], new byte[16]);
|
|
344 |
}
|
|
345 |
}
|
|
346 |
|
43972
|
347 |
/**
|
|
348 |
* Ensure that calling the original method from the substitution binds correctly.
|
|
349 |
*/
|
|
350 |
@Test
|
|
351 |
public void test1() {
|
|
352 |
test("test1Snippet", 1.0);
|
|
353 |
}
|
|
354 |
|
|
355 |
public double test1Snippet(double d) {
|
46459
|
356 |
return TestObject.next(d);
|
43972
|
357 |
}
|
|
358 |
|
|
359 |
/**
|
|
360 |
* Ensure that calling the substitution method binds to the original method properly.
|
|
361 |
*/
|
|
362 |
@Test
|
|
363 |
public void test2() {
|
|
364 |
test("test2Snippet", 1.0);
|
|
365 |
}
|
|
366 |
|
|
367 |
public double test2Snippet(double d) {
|
46459
|
368 |
return TestObject.next2(d);
|
43972
|
369 |
}
|
|
370 |
|
|
371 |
/**
|
|
372 |
* Ensure that substitution methods with assertions in them don't complain when the exception
|
|
373 |
* constructor is deleted.
|
|
374 |
*/
|
|
375 |
|
|
376 |
@Test
|
|
377 |
public void testNextAfter() {
|
55509
|
378 |
Assume.assumeFalse(JavaVersionUtil.JAVA_SPEC <= 8);
|
43972
|
379 |
double[] inArray = new double[1024];
|
|
380 |
double[] outArray = new double[1024];
|
|
381 |
for (int i = 0; i < inArray.length; i++) {
|
|
382 |
inArray[i] = -0.0;
|
|
383 |
}
|
|
384 |
test("doNextAfter", inArray, outArray);
|
|
385 |
}
|
|
386 |
|
|
387 |
public void doNextAfter(double[] outArray, double[] inArray) {
|
|
388 |
for (int i = 0; i < inArray.length; i++) {
|
|
389 |
double direction = (i & 1) == 0 ? Double.POSITIVE_INFINITY : -Double.NEGATIVE_INFINITY;
|
46459
|
390 |
outArray[i] = TestObject.nextAfter(inArray[i], direction);
|
43972
|
391 |
}
|
|
392 |
}
|
|
393 |
|
46509
|
394 |
private void testWithDifferentReturnValues(OptionValues options, String standardReturnValue, String compiledReturnValue, String name, Object... args) {
|
|
395 |
ResolvedJavaMethod method = getResolvedJavaMethod(name);
|
|
396 |
Object receiver = null;
|
|
397 |
|
|
398 |
Result expect = executeExpected(method, receiver, args);
|
|
399 |
Assert.assertEquals(standardReturnValue, expect.returnValue);
|
|
400 |
expect = new Result(compiledReturnValue, null);
|
|
401 |
testAgainstExpected(options, method, expect, receiver, args);
|
|
402 |
}
|
|
403 |
|
|
404 |
@Override
|
|
405 |
protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) {
|
|
406 |
return super.getCode(installedCodeOwner, graph, forceCompileOverride, installAsDefault, options);
|
|
407 |
}
|
|
408 |
|
|
409 |
boolean forceCompileOverride;
|
|
410 |
|
43972
|
411 |
@Test
|
|
412 |
public void testCallStringize() {
|
|
413 |
test("callStringize", "a string");
|
|
414 |
test("callStringize", Boolean.TRUE);
|
46509
|
415 |
// Unset 'exception seen' bit if testCallStringizeWithoutInlinePartialIntrinsicExit
|
|
416 |
// is executed before this test
|
|
417 |
getResolvedJavaMethod("callStringize").reprofile();
|
|
418 |
forceCompileOverride = true;
|
|
419 |
String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
|
|
420 |
String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
|
|
421 |
testWithDifferentReturnValues(getInitialOptions(), standardReturnValue, compiledReturnValue, "callStringize", THROW_EXCEPTION_MARKER);
|
|
422 |
}
|
|
423 |
|
|
424 |
@Test
|
|
425 |
public void testCallStringizeWithoutInlinePartialIntrinsicExit() {
|
|
426 |
OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false);
|
|
427 |
test(options, "callStringize", "a string");
|
|
428 |
test(options, "callStringize", Boolean.TRUE);
|
|
429 |
String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
|
|
430 |
String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
|
46536
|
431 |
forceCompileOverride = true;
|
|
432 |
inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
|
54601
|
433 |
inlineInvokeMethodName = "stringize";
|
46536
|
434 |
try {
|
|
435 |
testWithDifferentReturnValues(options, standardReturnValue, compiledReturnValue, "callStringize", THROW_EXCEPTION_MARKER);
|
|
436 |
} finally {
|
|
437 |
inlineInvokeDecision = null;
|
|
438 |
inlineInvokeMethodName = null;
|
46509
|
439 |
}
|
43972
|
440 |
}
|
|
441 |
|
46459
|
442 |
@Test
|
|
443 |
public void testCallStringizeId() {
|
|
444 |
test("callStringizeId", new TestObject("a string"));
|
|
445 |
test("callStringizeId", new TestObject(Boolean.TRUE));
|
46509
|
446 |
// Unset 'exception seen' bit if testCallStringizeIdWithoutInlinePartialIntrinsicExit
|
|
447 |
// is executed before this test
|
|
448 |
getResolvedJavaMethod("callStringize").reprofile();
|
|
449 |
forceCompileOverride = true;
|
|
450 |
String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
|
|
451 |
String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
|
|
452 |
testWithDifferentReturnValues(getInitialOptions(), standardReturnValue, compiledReturnValue, "callStringizeId", new TestObject(THROW_EXCEPTION_MARKER));
|
|
453 |
}
|
|
454 |
|
|
455 |
@Test
|
|
456 |
public void testCallStringizeIdWithoutInlinePartialIntrinsicExit() {
|
|
457 |
OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false);
|
|
458 |
test(options, "callStringizeId", new TestObject("a string"));
|
|
459 |
test(options, "callStringizeId", new TestObject(Boolean.TRUE));
|
|
460 |
TestObject exceptionTestObject = new TestObject(THROW_EXCEPTION_MARKER);
|
|
461 |
String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
|
|
462 |
String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
|
|
463 |
forceCompileOverride = true;
|
46536
|
464 |
inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
|
|
465 |
inlineInvokeMethodName = "stringizeId";
|
|
466 |
try {
|
|
467 |
testWithDifferentReturnValues(options, standardReturnValue, compiledReturnValue, "callStringizeId", exceptionTestObject);
|
|
468 |
} finally {
|
|
469 |
inlineInvokeDecision = null;
|
|
470 |
inlineInvokeMethodName = null;
|
|
471 |
}
|
46459
|
472 |
}
|
|
473 |
|
43972
|
474 |
public static Object callStringize(Object obj) {
|
46509
|
475 |
try {
|
|
476 |
return TestObject.stringize(obj);
|
|
477 |
} catch (CustomError e) {
|
|
478 |
if (GraalDirectives.inCompiledCode()) {
|
|
479 |
return IN_COMPILED_HANDLER_MARKER;
|
|
480 |
}
|
|
481 |
return IN_INTERPRETED_HANDLER_MARKER;
|
|
482 |
}
|
46459
|
483 |
}
|
|
484 |
|
|
485 |
public static Object callStringizeId(TestObject testObj) {
|
46509
|
486 |
try {
|
|
487 |
return testObj.stringizeId();
|
|
488 |
} catch (CustomError e) {
|
|
489 |
if (GraalDirectives.inCompiledCode()) {
|
|
490 |
return IN_COMPILED_HANDLER_MARKER;
|
|
491 |
}
|
|
492 |
return IN_INTERPRETED_HANDLER_MARKER;
|
|
493 |
}
|
43972
|
494 |
}
|
|
495 |
|
|
496 |
@Test
|
|
497 |
public void testRootCompileStringize() {
|
46459
|
498 |
ResolvedJavaMethod method = getResolvedJavaMethod(TestObject.class, "stringize");
|
43972
|
499 |
test(method, null, "a string");
|
|
500 |
test(method, null, Boolean.TRUE);
|
|
501 |
test(method, null, THROW_EXCEPTION_MARKER);
|
|
502 |
}
|
|
503 |
|
|
504 |
@Test
|
|
505 |
public void testLambda() {
|
|
506 |
test("callLambda", (String) null);
|
|
507 |
test("callLambda", "a string");
|
|
508 |
}
|
|
509 |
|
|
510 |
public static String callLambda(String value) {
|
46459
|
511 |
return TestObject.identity(value);
|
43972
|
512 |
}
|
46509
|
513 |
|
|
514 |
public static int callCopyFirst(byte[] in, byte[] out, boolean left2right) {
|
|
515 |
int res = TestObject.copyFirst(in, out, left2right);
|
|
516 |
if (res == 17) {
|
|
517 |
// A node after the intrinsic that needs a frame state.
|
|
518 |
GraalDirectives.deoptimize();
|
|
519 |
}
|
|
520 |
return res;
|
|
521 |
}
|
|
522 |
|
|
523 |
public static int callCopyFirstWrapper(byte[] in, byte[] out, boolean left2right) {
|
|
524 |
return callCopyFirst(in, out, left2right);
|
|
525 |
}
|
|
526 |
|
|
527 |
public static int callCopyFirstL2R(byte[] in, byte[] out) {
|
|
528 |
int res = TestObject.copyFirstL2R(in, out);
|
|
529 |
if (res == 17) {
|
|
530 |
// A node after the intrinsic that needs a frame state.
|
|
531 |
GraalDirectives.deoptimize();
|
|
532 |
}
|
|
533 |
return res;
|
|
534 |
}
|
|
535 |
|
|
536 |
@Test
|
|
537 |
public void testCallCopyFirst() {
|
|
538 |
byte[] in = {0, 1, 2, 3, 4};
|
|
539 |
byte[] out = new byte[in.length];
|
|
540 |
test("callCopyFirst", in, out, true);
|
|
541 |
test("callCopyFirst", in, out, false);
|
|
542 |
}
|
|
543 |
|
|
544 |
@SuppressWarnings("try")
|
|
545 |
@Test
|
|
546 |
public void testCallCopyFirstL2R() {
|
|
547 |
byte[] in = {0, 1, 2, 3, 4};
|
|
548 |
byte[] out = new byte[in.length];
|
|
549 |
try {
|
46640
|
550 |
test("callCopyFirstL2R", in, out);
|
46509
|
551 |
} catch (GraalGraphError e) {
|
|
552 |
assertTrue(e.getMessage().startsWith("Invalid frame state"));
|
|
553 |
}
|
|
554 |
}
|
|
555 |
|
|
556 |
@Override
|
|
557 |
protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
|
46536
|
558 |
if (inlineInvokeMethodName == null || inlineInvokeMethodName.equals(method.getName())) {
|
|
559 |
return inlineInvokeDecision;
|
|
560 |
}
|
|
561 |
return null;
|
46509
|
562 |
}
|
|
563 |
|
|
564 |
@Test
|
|
565 |
public void testCallCopyFirstWithoutInlinePartialIntrinsicExit() {
|
|
566 |
OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false);
|
|
567 |
inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
|
|
568 |
try {
|
|
569 |
byte[] in = {0, 1, 2, 3, 4};
|
|
570 |
byte[] out = new byte[in.length];
|
|
571 |
test(options, "callCopyFirstWrapper", in, out, true);
|
|
572 |
test(options, "callCopyFirstWrapper", in, out, false);
|
|
573 |
} finally {
|
|
574 |
inlineInvokeDecision = null;
|
|
575 |
}
|
|
576 |
}
|
46807
|
577 |
|
|
578 |
public static int nonVoidIntrinsicWithCall(int x, int y) {
|
|
579 |
if (TestObject.nonVoidIntrinsicWithCall(x, y) == x) {
|
|
580 |
GraalDirectives.deoptimize();
|
|
581 |
}
|
|
582 |
return y;
|
|
583 |
}
|
|
584 |
|
|
585 |
/**
|
|
586 |
* This tests the case where an intrinsic ends with a runtime call but returns some kind of
|
|
587 |
* value. This requires that a FrameState is available after the {@link ForeignCallNode} since
|
|
588 |
* the return value must be computed on return from the call.
|
|
589 |
*/
|
|
590 |
@Test
|
|
591 |
public void testNonVoidIntrinsicWithCall() {
|
|
592 |
testGraph("nonVoidIntrinsicWithCall");
|
|
593 |
}
|
|
594 |
|
|
595 |
public static int nonVoidIntrinsicWithOptimizedSplit(int x) {
|
|
596 |
if (TestObject.nonVoidIntrinsicWithOptimizedSplit(x) == x) {
|
|
597 |
GraalDirectives.deoptimize();
|
|
598 |
}
|
|
599 |
return x;
|
|
600 |
}
|
|
601 |
|
|
602 |
/**
|
|
603 |
* This is similar to {@link #testNonVoidIntrinsicWithCall()} but has a merge after the call
|
|
604 |
* which would normally capture the {@link FrameState} but in this case we force the merge to be
|
|
605 |
* optimized away.
|
|
606 |
*/
|
|
607 |
@Test
|
|
608 |
public void testNonVoidIntrinsicWithOptimizedSplit() {
|
|
609 |
testGraph("nonVoidIntrinsicWithOptimizedSplit");
|
|
610 |
}
|
|
611 |
|
54084
|
612 |
public static int div(int x, int y) {
|
|
613 |
return TestObject.div(x, y);
|
|
614 |
}
|
|
615 |
|
|
616 |
@Test
|
|
617 |
public void testAssertionInMethodSubstitution() {
|
|
618 |
try {
|
|
619 |
ResolvedJavaMethod method = getResolvedJavaMethod("div");
|
|
620 |
// avoid dumping graphs and printing exception since and exception is expected
|
|
621 |
OptionValues options = new OptionValues(getInitialOptions(), DumpOnError, false);
|
|
622 |
parse(new Builder(options, getDebugContext(options, null, method), AllowAssumptions.YES).method(method).compilationId(getCompilationId(method)), getEagerGraphBuilderSuite());
|
|
623 |
throw GraalError.shouldNotReachHere("BytecodeParser should have complained about using assertion in an intrinsic.");
|
|
624 |
} catch (BytecodeParserError e) {
|
|
625 |
// Expected behavior
|
|
626 |
}
|
|
627 |
}
|
|
628 |
|
46807
|
629 |
@SuppressWarnings("try")
|
|
630 |
private void testGraph(String name) {
|
|
631 |
StructuredGraph graph = parseEager(name, StructuredGraph.AllowAssumptions.YES);
|
|
632 |
try (DebugContext.Scope s0 = graph.getDebug().scope(name, graph)) {
|
|
633 |
for (OpaqueNode node : graph.getNodes().filter(OpaqueNode.class)) {
|
52578
|
634 |
node.remove();
|
46807
|
635 |
}
|
|
636 |
HighTierContext context = getDefaultHighTierContext();
|
|
637 |
CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
|
|
638 |
new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
|
|
639 |
new FloatingReadPhase().apply(graph);
|
|
640 |
canonicalizer.apply(graph, context);
|
|
641 |
new DeadCodeEliminationPhase().apply(graph);
|
|
642 |
new GuardLoweringPhase().apply(graph, getDefaultMidTierContext());
|
|
643 |
new FrameStateAssignmentPhase().apply(graph);
|
|
644 |
} catch (Throwable e) {
|
|
645 |
throw graph.getDebug().handle(e);
|
|
646 |
}
|
|
647 |
}
|
|
648 |
|
|
649 |
private class DummyInjectionProvider implements NodeIntrinsicPluginFactory.InjectionProvider {
|
|
650 |
@SuppressWarnings("unchecked")
|
|
651 |
@Override
|
|
652 |
public <T> T getInjectedArgument(Class<T> type) {
|
|
653 |
if (type == ForeignCallsProvider.class) {
|
|
654 |
return (T) new ForeignCallsProvider() {
|
|
655 |
@Override
|
|
656 |
public LIRKind getValueKind(JavaKind javaKind) {
|
|
657 |
return null;
|
|
658 |
}
|
|
659 |
|
|
660 |
@Override
|
|
661 |
public boolean isReexecutable(ForeignCallDescriptor descriptor) {
|
|
662 |
return false;
|
|
663 |
}
|
|
664 |
|
|
665 |
@Override
|
|
666 |
public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) {
|
|
667 |
return new LocationIdentity[0];
|
|
668 |
}
|
|
669 |
|
|
670 |
@Override
|
|
671 |
public boolean canDeoptimize(ForeignCallDescriptor descriptor) {
|
|
672 |
return false;
|
|
673 |
}
|
|
674 |
|
|
675 |
@Override
|
|
676 |
public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) {
|
|
677 |
return false;
|
|
678 |
}
|
|
679 |
|
|
680 |
@Override
|
|
681 |
public ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
|
|
682 |
return null;
|
|
683 |
}
|
54601
|
684 |
|
|
685 |
@Override
|
|
686 |
public boolean isAvailable(ForeignCallDescriptor descriptor) {
|
|
687 |
return true;
|
|
688 |
}
|
46807
|
689 |
};
|
|
690 |
}
|
|
691 |
if (type == SnippetReflectionProvider.class) {
|
|
692 |
return (T) getSnippetReflection();
|
|
693 |
}
|
|
694 |
return null;
|
|
695 |
}
|
|
696 |
|
|
697 |
@Override
|
|
698 |
public Stamp getInjectedStamp(Class<?> type, boolean nonNull) {
|
|
699 |
JavaKind kind = JavaKind.fromJavaClass(type);
|
|
700 |
return StampFactory.forKind(kind);
|
|
701 |
}
|
|
702 |
}
|
43972
|
703 |
}
|