Skip to content
This repository was archived by the owner on Sep 19, 2023. It is now read-only.

8279515: C1: No inlining through invokedynamic and invokestatic call sites when resolved class is not linked #80

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/hotspot/share/c1/c1_GraphBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2072,9 +2072,11 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
}

// check if we could do inlining
if (!PatchALot && Inline && target->is_loaded() && callee_holder->is_linked() && !patch_for_appendix) {
if (!PatchALot && Inline && target->is_loaded() && !patch_for_appendix &&
callee_holder->is_loaded()) { // the effect of symbolic reference resolution

// callee is known => check if we have static binding
if ((code == Bytecodes::_invokestatic && callee_holder->is_initialized()) || // invokestatic involves an initialization barrier on resolved klass
if ((code == Bytecodes::_invokestatic && klass->is_initialized()) || // invokestatic involves an initialization barrier on declaring class
code == Bytecodes::_invokespecial ||
(code == Bytecodes::_invokevirtual && target->is_final_method()) ||
code == Bytecodes::_invokedynamic) {
Expand Down
5 changes: 3 additions & 2 deletions src/hotspot/share/ci/ciStreams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,9 @@ ciKlass* ciBytecodeStream::get_declared_method_holder() {
constantPoolHandle cpool(THREAD, _method->get_Method()->constants());
bool ignore;
// report as MethodHandle for invokedynamic, which is syntactically classless
if (cur_bc() == Bytecodes::_invokedynamic)
return CURRENT_ENV->get_klass_by_name(_holder, ciSymbols::java_lang_invoke_MethodHandle(), false);
if (cur_bc() == Bytecodes::_invokedynamic) {
return CURRENT_ENV->MethodHandle_klass();
}
return CURRENT_ENV->get_klass_by_index(cpool, get_method_holder_index(), ignore, _holder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ private String getInternalName(Class<?> c) {
}

private static MemberName resolveFrom(String name, MethodType type, Class<?> holder) {
assert(!UNSAFE.shouldBeInitialized(holder)) : holder + "not initialized";
MemberName member = new MemberName(holder, name, type, REF_invokeStatic);
MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder, LM_TRUSTED);
traceLambdaForm(name, type, holder, resolvedMember);
Expand Down
158 changes: 158 additions & 0 deletions test/hotspot/jtreg/compiler/inlining/ResolvedClassTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2022, 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.
*/

/**
* @test
* @bug 8279515
*
* @requires vm.flagless
* @modules java.base/jdk.internal.misc
* @library /test/lib /
*
* @run driver compiler.jsr292.ResolvedClassTest
*/

package compiler.jsr292;

import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;

import java.io.IOException;

public class ResolvedClassTest {
/* ======================================================================== */
static void testStatic() throws IOException {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
"-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
"-Xbatch", "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly," + TestStatic.class.getName() + "::test",
TestStatic.class.getName());

OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());

analyzer.shouldHaveExitValue(0);

analyzer.shouldNotContain("TestStatic$A::m (1 bytes) not inlineable");
analyzer.shouldNotContain("TestStatic$A::m (1 bytes) no static binding");

analyzer.shouldContain("TestStatic$A::m (1 bytes) inline");
}

static class TestStatic {
static class A {
static void m() {}
}
static class B extends A {}

// @DontInline
static void test() {
B.m(); // invokestatic B "m" => A::m
}

public static void main(String[] args) {
for (int i = 0; i < 20_000; i++) {
test();
}
}
}

/* ======================================================================== */
static void testStaticInit() throws IOException {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
"-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
"-Xbatch", "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly," + TestStaticInit.class.getName() + "::test",
TestStaticInit.class.getName());

OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());

analyzer.shouldHaveExitValue(0);

analyzer.shouldContain("TestStaticInit$A::m (1 bytes) no static binding");
}

static class TestStaticInit {
static class A {
static {
for (int i = 0; i < 20_000; i++) {
TestStaticInit.test();
}
}

static void m() {}
}
static class B extends A {}

// @DontInline
static void test() {
B.m(); // A::<clinit> => test() => A::m()
}

public static void main(String[] args) {
A.m(); // trigger initialization of A
}
}

/* ======================================================================== */
static void testIndy() throws IOException {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
"-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
"-Xbatch", "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly," + TestIndy.class.getName() + "::test",
TestIndy.class.getName());

OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());

analyzer.shouldHaveExitValue(0);

analyzer.shouldNotContain("java.lang.invoke.Invokers$Holder::linkToTargetMethod (9 bytes) not inlineable");

analyzer.shouldContain("java.lang.invoke.Invokers$Holder::linkToTargetMethod (9 bytes) force inline by annotation");
analyzer.shouldContain("java/lang/invoke/MethodHandle::invokeBasic (not loaded) not inlineable");
}

static class TestIndy {
static String str = "";

// @DontInline
static void test() {
String s1 = "" + str; // indy (linked)

for (int i = 0; i < 200_000; i++) {} // trigger OSR compilation

String s2 = "" + str; // indy (not linked)
}

public static void main(String[] args) {
test();
}
}

/* ======================================================================== */

public static void main(String[] args) throws IOException {
testStatic();
testStaticInit();
testIndy();
}
}