/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.truffle.test;
import org.graalvm.compiler.code.SourceStackTraceBailoutException;
import org.graalvm.compiler.replacements.PEGraphDecoder;
import org.graalvm.compiler.truffle.OptimizedCallTarget;
import org.graalvm.compiler.truffle.TruffleCompilerOptions;
import org.graalvm.compiler.truffle.test.nodes.AbstractTestNode;
import org.graalvm.compiler.truffle.test.nodes.AddTestNode;
import org.graalvm.compiler.truffle.test.nodes.BlockTestNode;
import org.graalvm.compiler.truffle.test.nodes.ConstantTestNode;
import org.graalvm.compiler.truffle.test.nodes.ExplodeLoopUntilReturnNode;
import org.graalvm.compiler.truffle.test.nodes.ExplodeLoopUntilReturnWithThrowNode;
import org.graalvm.compiler.truffle.test.nodes.LambdaTestNode;
import org.graalvm.compiler.truffle.test.nodes.LoadLocalTestNode;
import org.graalvm.compiler.truffle.test.nodes.LoopTestNode;
import org.graalvm.compiler.truffle.test.nodes.NestedExplodedLoopTestNode;
import org.graalvm.compiler.truffle.test.nodes.NeverPartOfCompilationTestNode;
import org.graalvm.compiler.truffle.test.nodes.ObjectEqualsNode;
import org.graalvm.compiler.truffle.test.nodes.ObjectHashCodeNode;
import org.graalvm.compiler.truffle.test.nodes.RecursionTestNode;
import org.graalvm.compiler.truffle.test.nodes.RootTestNode;
import org.graalvm.compiler.truffle.test.nodes.StoreLocalTestNode;
import org.graalvm.compiler.truffle.test.nodes.StringEqualsNode;
import org.graalvm.compiler.truffle.test.nodes.StringHashCodeFinalNode;
import org.graalvm.compiler.truffle.test.nodes.StringHashCodeNonFinalNode;
import org.graalvm.compiler.truffle.test.nodes.SynchronizedExceptionMergeNode;
import org.graalvm.compiler.truffle.test.nodes.TwoMergesExplodedLoopTestNode;
import org.junit.Assert;
import org.junit.Test;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.nodes.RootNode;
import jdk.vm.ci.code.BailoutException;
public class SimplePartialEvaluationTest extends PartialEvaluationTest {
public static Object constant42() {
return 42;
}
@Test
public void constantValue() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new ConstantTestNode(42);
assertPartialEvalEquals("constant42", new RootTestNode(fd, "constantValue", result));
}
@Test
public void addConstants() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new AddTestNode(new ConstantTestNode(40), new ConstantTestNode(2));
assertPartialEvalEquals("constant42", new RootTestNode(fd, "addConstants", result));
}
@Test
public void neverPartOfCompilationTest() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode firstTree = new NeverPartOfCompilationTestNode(new ConstantTestNode(1), 2);
assertPartialEvalEquals("constant42", new RootTestNode(fd, "neverPartOfCompilationTest", firstTree));
AbstractTestNode secondTree = new NeverPartOfCompilationTestNode(new ConstantTestNode(1), 1);
try {
assertPartialEvalEquals("constant42", new RootTestNode(fd, "neverPartOfCompilationTest", secondTree));
Assert.fail("Expected verification error!");
} catch (SourceStackTraceBailoutException t) {
// Expected verification error occurred.
StackTraceElement[] trace = t.getStackTrace();
assertStack(trace[0], "org.graalvm.compiler.truffle.test.nodes.NeverPartOfCompilationTestNode", "execute", "NeverPartOfCompilationTestNode.java");
assertStack(trace[1], "org.graalvm.compiler.truffle.test.nodes.RootTestNode", "execute", "RootTestNode.java");
}
}
private static void assertStack(StackTraceElement stack, String className, String methodName, String fileName) {
Assert.assertEquals(className, stack.getClassName());
Assert.assertEquals(methodName, stack.getMethodName());
Assert.assertEquals(fileName, stack.getFileName());
}
@Test
public void nestedLoopExplosion() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new AddTestNode(new NestedExplodedLoopTestNode(5), new ConstantTestNode(17));
assertPartialEvalEquals("constant42", new RootTestNode(fd, "nestedLoopExplosion", result));
}
@Test
public void twoMergesLoopExplosion() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new AddTestNode(new TwoMergesExplodedLoopTestNode(5), new ConstantTestNode(37));
assertPartialEvalEquals("constant42", new RootTestNode(fd, "twoMergesLoopExplosion", result));
}
@Test
public void sequenceConstants() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new BlockTestNode(new AbstractTestNode[]{new ConstantTestNode(40), new ConstantTestNode(42)});
assertPartialEvalEquals("constant42", new RootTestNode(fd, "sequenceConstants", result));
}
@Test
public void localVariable() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new BlockTestNode(new AbstractTestNode[]{new StoreLocalTestNode("x", fd, new ConstantTestNode(42)), new LoadLocalTestNode("x", fd)});
assertPartialEvalEquals("constant42", new RootTestNode(fd, "localVariable", result));
}
@Test
public void longSequenceConstants() {
FrameDescriptor fd = new FrameDescriptor();
int length = 40;
AbstractTestNode[] children = new AbstractTestNode[length];
for (int i = 0; i < children.length; ++i) {
children[i] = new ConstantTestNode(42);
}
AbstractTestNode result = new BlockTestNode(children);
assertPartialEvalEquals("constant42", new RootTestNode(fd, "longSequenceConstants", result));
}
@Test
public void longAddConstants() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new ConstantTestNode(2);
for (int i = 0; i < 20; ++i) {
result = new AddTestNode(result, new ConstantTestNode(2));
}
assertPartialEvalEquals("constant42", new RootTestNode(fd, "longAddConstants", result));
}
@Test
public void mixLocalAndAdd() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new BlockTestNode(new AbstractTestNode[]{new StoreLocalTestNode("x", fd, new ConstantTestNode(40)),
new StoreLocalTestNode("x", fd, new AddTestNode(new LoadLocalTestNode("x", fd), new ConstantTestNode(2))), new LoadLocalTestNode("x", fd)});
assertPartialEvalEquals("constant42", new RootTestNode(fd, "mixLocalAndAdd", result));
}
@Test
public void loop() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new BlockTestNode(new AbstractTestNode[]{new StoreLocalTestNode("x", fd, new ConstantTestNode(0)),
new LoopTestNode(7, new StoreLocalTestNode("x", fd, new AddTestNode(new LoadLocalTestNode("x", fd), new ConstantTestNode(6))))});
assertPartialEvalEquals("constant42", new RootTestNode(fd, "loop", result));
}
@Test
public void longLoop() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new BlockTestNode(new AbstractTestNode[]{new StoreLocalTestNode("x", fd, new ConstantTestNode(0)),
new LoopTestNode(42, new StoreLocalTestNode("x", fd, new AddTestNode(new LoadLocalTestNode("x", fd), new ConstantTestNode(1))))});
RootTestNode rootNode = new RootTestNode(fd, "loop", result);
assertPartialEvalNoInvokes(rootNode);
assertPartialEvalEquals("constant42", rootNode);
}
@Test
public void lambda() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new LambdaTestNode();
assertPartialEvalEquals("constant42", new RootTestNode(fd, "constantValue", result));
}
@Test
public void allowedRecursion() {
/* Recursion depth just below the threshold that reports it as too deep recursion. */
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new RecursionTestNode(TruffleCompilerOptions.getValue(PEGraphDecoder.Options.InliningDepthError) - 5);
assertPartialEvalEquals("constant42", new RootTestNode(fd, "allowedRecursion", result));
}
@Test(expected = BailoutException.class)
public void tooDeepRecursion() {
/* Recursion depth just above the threshold that reports it as too deep recursion. */
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new RecursionTestNode(TruffleCompilerOptions.getValue(PEGraphDecoder.Options.InliningDepthError));
assertPartialEvalEquals("constant42", new RootTestNode(fd, "tooDeepRecursion", result));
}
@Test
public void intrinsicStatic() {
/*
* The intrinsic for String.equals() is inlined early during bytecode parsing, because we
* call equals() on a value that has the static type String.
*/
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new StringEqualsNode("abc", "abf");
RootNode rootNode = new RootTestNode(fd, "intrinsicStatic", result);
OptimizedCallTarget compilable = compileHelper("intrinsicStatic", rootNode, new Object[0]);
Assert.assertEquals(42, compilable.call(new Object[0]));
}
@Test
public void intrinsicVirtual() {
/*
* The intrinsic for String.equals() is inlined late during Truffle partial evaluation,
* because we call equals() on a value that has the static type Object, but during partial
* evaluation the more precise type String is known.
*/
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new ObjectEqualsNode("abc", "abf");
RootNode rootNode = new RootTestNode(fd, "intrinsicVirtual", result);
OptimizedCallTarget compilable = compileHelper("intrinsicVirtual", rootNode, new Object[0]);
Assert.assertEquals(42, compilable.call(new Object[0]));
}
@Test
public void intrinsicHashCode() {
/*
* The intrinsic for Object.hashCode() is inlined late during Truffle partial evaluation,
* because we call hashCode() on a value whose exact type Object is only known during
* partial evaluation.
*/
FrameDescriptor fd = new FrameDescriptor();
Object testObject = new Object();
AbstractTestNode result = new ObjectHashCodeNode(testObject);
RootNode rootNode = new RootTestNode(fd, "intrinsicHashCode", result);
OptimizedCallTarget compilable = compileHelper("intrinsicHashCode", rootNode, new Object[0]);
int actual = (Integer) compilable.call(new Object[0]);
int expected = testObject.hashCode();
Assert.assertEquals(expected, actual);
}
@Test
public void synchronizedExceptionMerge() {
/*
* Multiple non-inlineable methods with exception edges called from a synchronized method
* lead to a complicated Graal graph that involves the BytecodeFrame.UNWIND_BCI. This test
* checks that partial evaluation handles that case correctly.
*/
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new SynchronizedExceptionMergeNode();
RootNode rootNode = new RootTestNode(fd, "synchronizedExceptionMerge", result);
OptimizedCallTarget compilable = compileHelper("synchronizedExceptionMerge", rootNode, new Object[0]);
Assert.assertEquals(42, compilable.call(new Object[0]));
}
@Test
public void explodeLoopUntilReturn() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new ExplodeLoopUntilReturnNode();
assertPartialEvalEquals("constant42", new RootTestNode(fd, "explodeLoopUntilReturn", result));
}
@Test
public void explodeLoopUntilReturnWithThrow() {
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new ExplodeLoopUntilReturnWithThrowNode();
assertPartialEvalEquals("constant42", new RootTestNode(fd, "explodeLoopUntilReturnWithThrow", result));
}
@Test
public void intrinsicStringHashCodeFinal() {
/* The intrinsic for String.hashcode() triggers on constant strings. */
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new StringHashCodeFinalNode("*");
/* The hash code of "*" is 42. */
assertPartialEvalEquals("constant42", new RootTestNode(fd, "intrinsicStringHashCodeFinal", result));
}
@Test
public void intrinsicStringHashCodeNonFinal() {
/*
* The intrinsic for String.hashcode() does not trigger on non-constant strings, so the
* method String.hashCode() must be inlined during partial evaluation (so there must not be
* an invoke after partial evaluation).
*/
FrameDescriptor fd = new FrameDescriptor();
AbstractTestNode result = new StringHashCodeNonFinalNode("*");
assertPartialEvalNoInvokes(new RootTestNode(fd, "intrinsicStringHashCodeNonFinal", result));
}
}