/*
* Copyright (c) 2013, 2015, 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 static org.graalvm.compiler.truffle.TruffleCompilerOptions.TruffleFunctionInlining;
import static org.graalvm.compiler.truffle.TruffleCompilerOptions.TruffleInliningMaxCallerSize;
import static org.graalvm.compiler.truffle.TruffleCompilerOptions.TruffleMaximumRecursiveInlining;
import static org.graalvm.compiler.truffle.TruffleCompilerOptions.overrideOptions;
import org.graalvm.compiler.truffle.TruffleCompilerOptions;
import org.graalvm.compiler.truffle.TruffleInlining;
import org.junit.Assert;
import org.junit.Test;
public class BasicTruffleInliningTest extends TruffleInliningTest {
@Test
public void testSimpleInline() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
target("caller").
calls("callee").
buildDecisions();
// @formatter:on
assertInlined(decisions, "callee");
}
@Test
public void testMultipleInline() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
target("caller").
calls("callee").
calls("callee").
buildDecisions();
// @formatter:on
Assert.assertTrue(countInlines(decisions, "callee") == 2);
int inlineCount = 100;
builder.target("callee").target("caller", inlineCount);
for (int i = 0; i < inlineCount; i++) {
builder.calls("callee");
}
Assert.assertEquals(inlineCount, countInlines(builder.buildDecisions(), "callee"));
}
@Test
public void testInlineBigFunctions() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee", TruffleCompilerOptions.getValue(TruffleInliningMaxCallerSize) - 3).
target("caller").
calls("callee").
buildDecisions();
// @formatter:on
assertInlined(decisions, "callee");
}
@Test
public void testDontInlineBigFunctions() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee", TruffleCompilerOptions.getValue(TruffleInliningMaxCallerSize)).
target("caller").
calls("callee").
buildDecisions();
// @formatter:on
assertNotInlined(decisions, "callee");
}
@Test
public void testInlineIntoBigFunctions() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
target("caller", TruffleCompilerOptions.getValue(TruffleInliningMaxCallerSize) - 3).
calls("callee").
buildDecisions();
// @formatter:on
assertInlined(decisions, "callee");
}
@Test
public void testDontInlineIntoBigFunctions() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
target("caller", TruffleCompilerOptions.getValue(TruffleInliningMaxCallerSize)).
calls("callee").
buildDecisions();
// @formatter:on
assertNotInlined(decisions, "callee");
}
@Test
public void testRecursiveInline() {
TruffleInlining decisions = builder.target("recursive").calls("recursive").buildDecisions();
Assert.assertEquals(TruffleCompilerOptions.getValue(TruffleMaximumRecursiveInlining).intValue(), countInlines(decisions, "recursive"));
}
@Test
public void testDoubleRecursiveInline() {
TruffleInlining decisions = builder.target("recursive").calls("recursive").calls("recursive").buildDecisions();
int n = TruffleCompilerOptions.getValue(TruffleMaximumRecursiveInlining).intValue();
long geometricSum = 2 * (1 - ((long) Math.pow(2, n))) / (1 - 2); // sum of geometric
// progression a*r^n is
// (a(1-r^n))/(1-r)
// for 2*2^n it is
// 2*(1-2^n)/(1-2)
Assert.assertEquals(geometricSum, countInlines(decisions, "recursive"));
}
@Test
public void testIndirectRecursiveInline() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
calls("recursive").
target("recursive").
calls("callee").
buildDecisions();
// @formatter:on
Assert.assertEquals(TruffleCompilerOptions.getValue(TruffleMaximumRecursiveInlining).intValue(), countInlines(decisions, "recursive"));
Assert.assertEquals(TruffleCompilerOptions.getValue(TruffleMaximumRecursiveInlining) + 1, countInlines(decisions, "callee"));
}
@Test
public void testInlineBigWithCallSites() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee", (TruffleCompilerOptions.getValue(TruffleInliningMaxCallerSize) / 3) - 3).
target("caller").
calls("callee").
calls("callee").
calls("callee").
buildDecisions(true);
// @formatter:on
Assert.assertEquals(3, countInlines(decisions, "callee"));
}
@Test
public void testDontInlineBigWithCallSites() {
// Do not inline a function if it's size * cappedCallSites is too big
// @formatter:off
TruffleInlining decisions = builder.
target("callee", TruffleCompilerOptions.getValue(TruffleInliningMaxCallerSize) / 3).
target("caller").
calls("callee").
calls("callee").
calls("callee").
buildDecisions(true);
// @formatter:on
assertNotInlined(decisions, "callee");
Assert.assertTrue("Wrong reason for not inlining!", decisions.getCallSites().get(0).getProfile().getFailedReason().startsWith("deepNodeCount * callSites >"));
}
@Test
public void testDeepInline() {
// Limited to 14 at the moment because of TruffleInlining:97
int depth = 14;
builder.target("0");
for (Integer count = 0; count < depth; count++) {
Integer nextCount = count + 1;
builder.target(nextCount.toString()).calls(count.toString());
}
final int[] inlineDepth = {0};
TruffleInlining decisions = builder.buildDecisions();
traverseDecisions(decisions.getCallSites(), decision -> {
Assert.assertTrue(decision.isInline());
inlineDepth[0]++;
});
Assert.assertEquals(depth, inlineDepth[0]);
}
@Test
public void testWideInline() {
int width = 1000;
builder.target("leaf").target("main");
for (Integer i = 0; i < width; i++) {
builder.calls("leaf");
}
TruffleInlining decisions = builder.buildDecisions();
Assert.assertEquals(width, countInlines(decisions, "leaf"));
}
@Test
public void testFrequency() {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
target("caller").execute(4).
calls("callee", 2).
buildDecisions();
// @formatter:on
assertInlined(decisions, "callee");
Assert.assertEquals(0.5, decisions.getCallSites().get(0).getProfile().getFrequency(), 0);
}
@Test
@SuppressWarnings("try")
public void testTruffleFunctionInliningFlag() {
try (TruffleCompilerOptions.TruffleOptionsOverrideScope scope = overrideOptions(TruffleFunctionInlining, false)) {
// @formatter:off
TruffleInlining decisions = builder.
target("callee").
target("caller").
calls("callee", 2).
buildDecisions();
// @formatter:on
Assert.assertTrue("Decisions where made!", decisions.getCallSites().isEmpty());
}
}
}