mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8372634: C2: Materialize type information from instanceof checks
Reviewed-by: dlong, qamai, roland
This commit is contained in:
parent
4e9525ef36
commit
f2e56e4c18
@ -561,6 +561,20 @@ bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectiveSet::should_delay_inline(ciMethod* inlinee) {
|
||||
inlinee->check_is_loaded();
|
||||
VM_ENTRY_MARK;
|
||||
methodHandle mh(THREAD, inlinee->get_Method());
|
||||
|
||||
if (_inlinematchers != nullptr) {
|
||||
return matches_inline(mh, InlineMatcher::delay_inline);
|
||||
}
|
||||
if (!CompilerDirectivesIgnoreCompileCommandsOption) {
|
||||
return CompilerOracle::should_delay_inline(mh);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectiveSet::parse_and_add_inline(char* str, const char*& error_msg) {
|
||||
InlineMatcher* m = InlineMatcher::parse_inline_pattern(str, error_msg);
|
||||
if (m != nullptr) {
|
||||
|
||||
@ -142,6 +142,7 @@ public:
|
||||
void append_inline(InlineMatcher* m);
|
||||
bool should_inline(ciMethod* inlinee);
|
||||
bool should_not_inline(ciMethod* inlinee);
|
||||
bool should_delay_inline(ciMethod* inlinee);
|
||||
void print_inline(outputStream* st);
|
||||
DirectiveSet* compilecommand_compatibility_init(const methodHandle& method);
|
||||
bool is_exclusive_copy() { return _directive == nullptr; }
|
||||
|
||||
@ -480,6 +480,10 @@ bool CompilerOracle::should_not_inline(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::DontInline, method) || check_predicate(CompileCommandEnum::Exclude, method);
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_delay_inline(const methodHandle& method) {
|
||||
return (check_predicate(CompileCommandEnum::DelayInline, method));
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_print(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::Print, method);
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ class methodHandle;
|
||||
option(Log, "log", Bool) \
|
||||
option(Print, "print", Bool) \
|
||||
option(Inline, "inline", Bool) \
|
||||
option(DelayInline, "delayinline", Bool) \
|
||||
option(DontInline, "dontinline", Bool) \
|
||||
option(Blackhole, "blackhole", Bool) \
|
||||
option(CompileOnly, "compileonly", Bool)\
|
||||
@ -150,6 +151,9 @@ class CompilerOracle : AllStatic {
|
||||
// Tells whether we want to disallow inlining of this method
|
||||
static bool should_not_inline(const methodHandle& method);
|
||||
|
||||
// Tells whether we want to delay inlining of this method
|
||||
static bool should_delay_inline(const methodHandle& method);
|
||||
|
||||
// Tells whether this method changes Thread.currentThread()
|
||||
static bool changes_current_thread(const methodHandle& method);
|
||||
|
||||
|
||||
@ -100,6 +100,7 @@ public:
|
||||
enum InlineType {
|
||||
unknown_inline,
|
||||
dont_inline,
|
||||
delay_inline,
|
||||
force_inline
|
||||
};
|
||||
|
||||
|
||||
@ -984,7 +984,8 @@ public:
|
||||
JVMState* jvms, bool allow_inline, float profile_factor, ciKlass* speculative_receiver_type = nullptr,
|
||||
bool allow_intrinsics = true);
|
||||
bool should_delay_inlining(ciMethod* call_method, JVMState* jvms) {
|
||||
return should_delay_string_inlining(call_method, jvms) ||
|
||||
return C->directive()->should_delay_inline(call_method) ||
|
||||
should_delay_string_inlining(call_method, jvms) ||
|
||||
should_delay_boxing_inlining(call_method, jvms) ||
|
||||
should_delay_vector_inlining(call_method, jvms);
|
||||
}
|
||||
|
||||
@ -192,7 +192,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
// Try inlining a bytecoded method:
|
||||
if (!call_does_dispatch) {
|
||||
InlineTree* ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method());
|
||||
bool should_delay = C->should_delay_inlining();
|
||||
bool should_delay = C->should_delay_inlining() || C->directive()->should_delay_inline(callee);
|
||||
if (ilt->ok_to_inline(callee, jvms, profile, should_delay)) {
|
||||
CallGenerator* cg = CallGenerator::for_inline(callee, expected_uses);
|
||||
// For optimized virtual calls assert at runtime that receiver object
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include "opto/opaquenode.hpp"
|
||||
#include "opto/parse.hpp"
|
||||
#include "opto/runtime.hpp"
|
||||
#include "opto/subtypenode.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
|
||||
@ -1719,38 +1720,108 @@ static Node* extract_obj_from_klass_load(PhaseGVN* gvn, Node* n) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Matches exact and inexact type check IR shapes during parsing.
|
||||
// On successful match, returns type checked object node and its type after successful check
|
||||
// as out parameters.
|
||||
static bool match_type_check(PhaseGVN& gvn,
|
||||
BoolTest::mask btest,
|
||||
Node* con, const Type* tcon,
|
||||
Node* val, const Type* tval,
|
||||
Node** obj, const TypeOopPtr** cast_type) { // out-parameters
|
||||
// Look for opportunities to sharpen the type of a node whose klass is compared with a constant klass.
|
||||
// The constant klass being tested against can come from many bytecode instructions (implicitly or explicitly),
|
||||
// and also from profile data used by speculative casts.
|
||||
if (btest == BoolTest::eq && tcon->isa_klassptr()) {
|
||||
// Found:
|
||||
// Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq])
|
||||
// or the narrowOop equivalent.
|
||||
(*obj) = extract_obj_from_klass_load(&gvn, val);
|
||||
(*cast_type) = tcon->isa_klassptr()->as_instance_type();
|
||||
return true; // found
|
||||
}
|
||||
|
||||
// Match an instanceof check.
|
||||
// During parsing its IR shape is not canonicalized yet.
|
||||
//
|
||||
// obj superklass
|
||||
// | |
|
||||
// SubTypeCheck
|
||||
// |
|
||||
// Bool [eq] / [ne]
|
||||
// |
|
||||
// If
|
||||
// / \
|
||||
// T F
|
||||
// \ /
|
||||
// Region
|
||||
// \ ConI ConI
|
||||
// \ | /
|
||||
// val -> Phi ConI <- con
|
||||
// \ /
|
||||
// CmpI
|
||||
// |
|
||||
// Bool [btest]
|
||||
// |
|
||||
//
|
||||
if (tval->isa_int() && val->is_Phi() && val->in(0)->as_Region()->is_diamond()) {
|
||||
RegionNode* diamond = val->in(0)->as_Region();
|
||||
IfNode* if1 = diamond->in(1)->in(0)->as_If();
|
||||
BoolNode* b1 = if1->in(1)->isa_Bool();
|
||||
if (b1 != nullptr && b1->in(1)->isa_SubTypeCheck()) {
|
||||
assert(b1->_test._test == BoolTest::eq ||
|
||||
b1->_test._test == BoolTest::ne, "%d", b1->_test._test);
|
||||
|
||||
ProjNode* success_proj = if1->proj_out(b1->_test._test == BoolTest::eq ? 1 : 0);
|
||||
int idx = diamond->find_edge(success_proj);
|
||||
assert(idx == 1 || idx == 2, "");
|
||||
Node* vcon = val->in(idx);
|
||||
|
||||
assert(val->find_edge(con) > 0, "");
|
||||
if ((btest == BoolTest::eq && vcon == con) || (btest == BoolTest::ne && vcon != con)) {
|
||||
SubTypeCheckNode* sub = b1->in(1)->as_SubTypeCheck();
|
||||
Node* obj_or_subklass = sub->in(SubTypeCheckNode::ObjOrSubKlass);
|
||||
Node* superklass = sub->in(SubTypeCheckNode::SuperKlass);
|
||||
|
||||
if (gvn.type(obj_or_subklass)->isa_oopptr()) {
|
||||
const TypeKlassPtr* klass_ptr_type = gvn.type(superklass)->is_klassptr();
|
||||
const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve();
|
||||
|
||||
(*obj) = obj_or_subklass;
|
||||
(*cast_type) = improved_klass_ptr_type->cast_to_exactness(false)->as_instance_type();
|
||||
return true; // found
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
void Parse::sharpen_type_after_if(BoolTest::mask btest,
|
||||
Node* con, const Type* tcon,
|
||||
Node* val, const Type* tval) {
|
||||
// Look for opportunities to sharpen the type of a node
|
||||
// whose klass is compared with a constant klass.
|
||||
if (btest == BoolTest::eq && tcon->isa_klassptr()) {
|
||||
Node* obj = extract_obj_from_klass_load(&_gvn, val);
|
||||
const TypeOopPtr* con_type = tcon->isa_klassptr()->as_instance_type();
|
||||
if (obj != nullptr && (con_type->isa_instptr() || con_type->isa_aryptr())) {
|
||||
// Found:
|
||||
// Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq])
|
||||
// or the narrowOop equivalent.
|
||||
const Type* obj_type = _gvn.type(obj);
|
||||
const TypeOopPtr* tboth = obj_type->join_speculative(con_type)->isa_oopptr();
|
||||
if (tboth != nullptr && tboth->klass_is_exact() && tboth != obj_type &&
|
||||
tboth->higher_equal(obj_type)) {
|
||||
// obj has to be of the exact type Foo if the CmpP succeeds.
|
||||
int obj_in_map = map()->find_edge(obj);
|
||||
JVMState* jvms = this->jvms();
|
||||
if (obj_in_map >= 0 &&
|
||||
(jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
|
||||
TypeNode* ccast = new CheckCastPPNode(control(), obj, tboth);
|
||||
const Type* tcc = ccast->as_Type()->type();
|
||||
assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
|
||||
// Delay transform() call to allow recovery of pre-cast value
|
||||
// at the control merge.
|
||||
_gvn.set_type_bottom(ccast);
|
||||
record_for_igvn(ccast);
|
||||
// Here's the payoff.
|
||||
replace_in_map(obj, ccast);
|
||||
}
|
||||
}
|
||||
Node* obj = nullptr;
|
||||
const TypeOopPtr* cast_type = nullptr;
|
||||
// Insert a cast node with a narrowed type after a successful type check.
|
||||
if (match_type_check(_gvn, btest, con, tcon, val, tval,
|
||||
&obj, &cast_type)) {
|
||||
assert(obj != nullptr && cast_type != nullptr, "missing type check info");
|
||||
const Type* obj_type = _gvn.type(obj);
|
||||
const TypeOopPtr* tboth = obj_type->join_speculative(cast_type)->isa_oopptr();
|
||||
if (tboth != nullptr && tboth != obj_type && tboth->higher_equal(obj_type)) {
|
||||
int obj_in_map = map()->find_edge(obj);
|
||||
JVMState* jvms = this->jvms();
|
||||
if (obj_in_map >= 0 &&
|
||||
(jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
|
||||
TypeNode* ccast = new CheckCastPPNode(control(), obj, tboth);
|
||||
const Type* tcc = ccast->as_Type()->type();
|
||||
assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
|
||||
// Delay transform() call to allow recovery of pre-cast value
|
||||
// at the control merge.
|
||||
_gvn.set_type_bottom(ccast);
|
||||
record_for_igvn(ccast);
|
||||
// Here's the payoff.
|
||||
replace_in_map(obj, ccast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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
|
||||
* @summary 8372634
|
||||
*
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib /
|
||||
*
|
||||
* @run driver compiler.inlining.TestSubtypeCheckTypeInfo
|
||||
*/
|
||||
package compiler.inlining;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TestSubtypeCheckTypeInfo {
|
||||
static final Class<TestSubtypeCheckTypeInfo> THIS_CLASS = TestSubtypeCheckTypeInfo.class;
|
||||
static final String TEST_CLASS_NAME = THIS_CLASS.getName();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
|
||||
"-XX:-TieredCompilation", "-Xbatch", "-XX:CICompilerCount=1",
|
||||
"-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
|
||||
"-XX:CompileCommand=quiet",
|
||||
"-XX:CompileCommand=compileonly," + TEST_CLASS_NAME + "::test*",
|
||||
"-XX:CompileCommand=delayinline," + TEST_CLASS_NAME + "::lateInline*",
|
||||
TestSubtypeCheckTypeInfo.Launcher.class.getName());
|
||||
|
||||
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
|
||||
|
||||
analyzer.shouldHaveExitValue(0);
|
||||
|
||||
// The test is applicable only to C2 (present in Server VM).
|
||||
if (analyzer.getStderr().contains("Server VM")) {
|
||||
List<String> output = analyzer.asLinesWithoutVMWarnings();
|
||||
|
||||
parseOutput(output);
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
}
|
||||
|
||||
static class Launcher {
|
||||
public static void main(String[] args) {
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOf);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfCondPre);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfCondPost);
|
||||
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstance);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceCondPre);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceCondPost);
|
||||
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfLate);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfLateCondPre);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfLateCondPost);
|
||||
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceLate);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceLateCondPre);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceLateCondPost);
|
||||
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfCondLate);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfCondLatePre);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfCondLatePost);
|
||||
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceCondLate);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceCondLatePre);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceCondLatePost);
|
||||
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testInstanceOfNulls);
|
||||
runTestCase(TestSubtypeCheckTypeInfo::testIsInstanceNulls);
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineSuccess
|
||||
// @ 8 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testInstanceOf(A o, boolean cond) {
|
||||
if (o instanceof B) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineSuccess
|
||||
// @ 12 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testInstanceOfCondPre(A o, boolean cond) {
|
||||
if (cond && (o instanceof B)) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineSuccess
|
||||
// @ 12 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testInstanceOfCondPost(A o, boolean cond) {
|
||||
if ((o instanceof B) && cond) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineSuccess
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 3 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 10 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testIsInstance(A o, boolean cond) {
|
||||
if (B.class.isInstance(o)) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineSuccess
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 7 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 14 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testIsInstanceCondPre(A o, boolean cond) {
|
||||
if (cond && B.class.isInstance(o)) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineSuccess
|
||||
// @ 3 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 14 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testIsInstanceCondPost(A o, boolean cond) {
|
||||
if (B.class.isInstance(o) && cond) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineSuccess
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo::lateInline (9 bytes) inline (hot) late inline succeeded
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testInstanceOfLate(A o, boolean cond) {
|
||||
// if (o instanceof B) { o.m(); }
|
||||
lateInline(o, o instanceof B);
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// @ 17 compiler.inlining.TestSubtypeCheckTypeInfo::lateInline (9 bytes) inline (hot) late inline succeeded
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testInstanceOfLateCondPre(A o, boolean cond) {
|
||||
// if (cond && o instanceof B) { o.m(); }
|
||||
lateInline(o, cond && (o instanceof B));
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// @ 17 compiler.inlining.TestSubtypeCheckTypeInfo::lateInline (9 bytes) inline (hot) late inline succeeded
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testInstanceOfLateCondPost(A o, boolean cond) {
|
||||
// if ((o instanceof B) && cond) { o.m(); }
|
||||
lateInline(o, (o instanceof B) && cond);
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineSuccess
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 4 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 7 compiler.inlining.TestSubtypeCheckTypeInfo::lateInline (9 bytes) inline (hot) late inline succeeded
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo$B::m (1 bytes) inline (hot)
|
||||
static void testIsInstanceLate(A o, boolean cond) {
|
||||
// if (B.class.isInstance(o)) { o.m(); }
|
||||
lateInline(o, B.class.isInstance(o));
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 8 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 19 compiler.inlining.TestSubtypeCheckTypeInfo::lateInline (9 bytes) inline (hot) late inline succeeded
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testIsInstanceLateCondPre(A o, boolean cond) {
|
||||
// if (cond && B.class.isInstance(o)) { o.m(); }
|
||||
lateInline(o, cond && (B.class.isInstance(o)));
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 4 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 19 compiler.inlining.TestSubtypeCheckTypeInfo::lateInline (9 bytes) inline (hot) late inline succeeded
|
||||
// @ 5 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testIsInstanceLateCondPost(A o, boolean cond) {
|
||||
// if (B.class.isInstance(o) && cond) { o.m(); }
|
||||
lateInline(o, (B.class.isInstance(o) && cond));
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineFailure
|
||||
// @ 2 compiler.inlining.TestSubtypeCheckTypeInfo::lateInlineInstanceOfCondPre (17 bytes) inline (hot) late inline succeeded
|
||||
// @ 9 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testInstanceOfCondLate(A a, boolean cond) {
|
||||
if (lateInlineInstanceOfCondPre(a, true)) {
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// @ 2 compiler.inlining.TestSubtypeCheckTypeInfo::lateInlineInstanceOfCondPre (17 bytes) inline (hot) late inline succeeded
|
||||
// @ 9 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testInstanceOfCondLatePre(A a, boolean cond) {
|
||||
if (lateInlineInstanceOfCondPre(a, cond)) {
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// @ 2 compiler.inlining.TestSubtypeCheckTypeInfo::lateInlineInstanceOfCondPost (17 bytes) inline (hot) late inline succeeded
|
||||
// @ 9 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testInstanceOfCondLatePost(A a, boolean cond) {
|
||||
if (lateInlineInstanceOfCondPost(a, cond)) {
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineFailure
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 2 compiler.inlining.TestSubtypeCheckTypeInfo::lateInlineIsInstanceCondPre (19 bytes) inline (hot) late inline succeeded
|
||||
// @ 7 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 9 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testIsInstanceCondLate(A a, boolean cond) {
|
||||
if (lateInlineIsInstanceCondPre(a, true)) {
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 2 compiler.inlining.TestSubtypeCheckTypeInfo::lateInlineIsInstanceCondPre (19 bytes) inline (hot) late inline succeeded
|
||||
// @ 7 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 9 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testIsInstanceCondLatePre(A a, boolean cond) {
|
||||
if (lateInlineIsInstanceCondPre(a, cond)) {
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
// Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 2 compiler.inlining.TestSubtypeCheckTypeInfo::lateInlineIsInstanceCondPost (19 bytes) inline (hot) late inline succeeded
|
||||
// @ 3 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 9 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testIsInstanceCondLatePost(A a, boolean cond) {
|
||||
if (lateInlineIsInstanceCondPost(a, cond)) {
|
||||
a.m();
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
@InlineFailure
|
||||
// @ 20 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testInstanceOfNulls(A o, boolean cond) {
|
||||
A recv = (cond ? o : null);
|
||||
if (recv instanceof B) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
@InlineFailure
|
||||
//Inlining _isInstance on constant Class compiler/inlining/TestSubtypeCheckTypeInfo$B
|
||||
// @ 13 java.lang.Class::isInstance (0 bytes) (intrinsic)
|
||||
// @ 20 compiler.inlining.TestSubtypeCheckTypeInfo$A::m (0 bytes) failed to inline: virtual call
|
||||
static void testIsInstanceNulls(A o, boolean cond) {
|
||||
A recv = (cond ? o : null);
|
||||
if (B.class.isInstance(recv)) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
static abstract class A {
|
||||
public abstract void m();
|
||||
}
|
||||
static abstract class B extends A {
|
||||
public void m() {}
|
||||
}
|
||||
|
||||
static class C extends A {
|
||||
public void m() {}
|
||||
}
|
||||
|
||||
static void lateInline(A o, boolean cond) {
|
||||
if (cond) {
|
||||
o.m();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean lateInlineInstanceOfCondPre(A o, boolean cond) {
|
||||
return cond && (o instanceof B);
|
||||
}
|
||||
|
||||
static boolean lateInlineInstanceOfCondPost(A o, boolean cond) {
|
||||
return (o instanceof B) && cond;
|
||||
}
|
||||
|
||||
static boolean lateInlineIsInstanceCondPre(A o, boolean cond) {
|
||||
return cond && B.class.isInstance(o);
|
||||
}
|
||||
static boolean lateInlineIsInstanceCondPost(A o, boolean cond) {
|
||||
return B.class.isInstance(o) && cond;
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
static final String INLINE_SUCCESS_MESSAGE = "B::m (1 bytes) inline (hot)";
|
||||
static final String INLINE_FAILURE_MESSAGE = "A::m (0 bytes) failed to inline: virtual call";
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface InlineSuccess {
|
||||
String[] shouldContain() default INLINE_SUCCESS_MESSAGE;
|
||||
String[] shouldNotContain() default INLINE_FAILURE_MESSAGE;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface InlineFailure {
|
||||
String[] shouldContain() default INLINE_FAILURE_MESSAGE;
|
||||
String[] shouldNotContain() default INLINE_SUCCESS_MESSAGE;
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
// Parse compilation log (-XX:+PrintCompilation -XX:+PrintInlining output).
|
||||
static void parseOutput(List<String> output) {
|
||||
Pattern compilation = Pattern.compile("^\\d+\\s+\\d+.*");
|
||||
StringBuilder inlineTree = new StringBuilder();
|
||||
Set<String> passedTests = new HashSet();
|
||||
Set<String> failedTests = new HashSet();
|
||||
for (String line : output) {
|
||||
// Detect start of next compilation.
|
||||
if (compilation.matcher(line).matches()) {
|
||||
// Parse output for previous compilation.
|
||||
validateInliningOutput(inlineTree.toString(), passedTests, failedTests);
|
||||
inlineTree = new StringBuilder(); // reset
|
||||
}
|
||||
inlineTree.append(line);
|
||||
}
|
||||
// Process last compilation
|
||||
validateInliningOutput(inlineTree.toString(), passedTests, failedTests);
|
||||
|
||||
if (!failedTests.isEmpty()) {
|
||||
String msg = String.format("TEST FAILED: %d test cases failed", failedTests.size());
|
||||
throw new AssertionError(msg);
|
||||
} else if (passedTests.size() != totalTestCount()) {
|
||||
String msg = String.format("TEST FAILED: %d out of %d test cases passed", passedTests.size(), totalTestCount());
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Sample:
|
||||
// 213 42 b compiler.inlining.TestSubtypeCheckTypeInfo::testIsInstanceCondLatePost (13 bytes)
|
||||
static final Pattern TEST_CASE = Pattern.compile("^\\d+\\s+\\d+\\s+b\\s+" + TEST_CLASS_NAME + "::(\\w+) .*");
|
||||
|
||||
static boolean validateInliningOutput(String inlineTree, Set<String> passedTests, Set<String> failedTests) {
|
||||
Matcher m = TEST_CASE.matcher(inlineTree);
|
||||
if (m.matches()) {
|
||||
String testName = m.group(1);
|
||||
System.out.print(testName);
|
||||
try {
|
||||
Method testMethod = TestSubtypeCheckTypeInfo.class.getDeclaredMethod(testName, A.class, boolean.class);
|
||||
if (validate(inlineTree, testMethod.getAnnotation(InlineSuccess.class)) &&
|
||||
validate(inlineTree, testMethod.getAnnotation(InlineFailure.class))) {
|
||||
System.out.println(": SUCCESS");
|
||||
passedTests.add(testName);
|
||||
return true;
|
||||
} else {
|
||||
failedTests.add(testName);
|
||||
return false;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.out.println(": FAILURE: Missing test info for " + testName + ": " + inlineTree);
|
||||
throw new InternalError(e);
|
||||
}
|
||||
} else {
|
||||
return false; // not a test method; ignored
|
||||
}
|
||||
}
|
||||
|
||||
static boolean validate(String message, InlineSuccess ann) {
|
||||
if (ann != null) {
|
||||
return validatePatterns(message, ann.shouldContain(), ann.shouldNotContain());
|
||||
}
|
||||
return true; // no patterns to validate
|
||||
}
|
||||
|
||||
static boolean validate(String message, InlineFailure ann) {
|
||||
if (ann != null) {
|
||||
return validatePatterns(message, ann.shouldContain(), ann.shouldNotContain());
|
||||
}
|
||||
return true; // no patterns to validate
|
||||
}
|
||||
|
||||
static boolean validatePatterns(String message, String[] shouldContain, String[] shouldNotContain) {
|
||||
for (String pattern : shouldContain) {
|
||||
if (!message.contains(pattern)) {
|
||||
System.out.printf(": FAILURE: '%s' not found in '%s'\n", pattern, message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (String pattern : shouldNotContain) {
|
||||
if (message.contains(pattern)) {
|
||||
System.out.printf(": FAILURE: '%s' found in '%s'\n", pattern, message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int totalTestCount() {
|
||||
int count = 0;
|
||||
for (Method m : THIS_CLASS.getDeclaredMethods()) {
|
||||
if (m.isAnnotationPresent(InlineSuccess.class) || m.isAnnotationPresent(InlineFailure.class)) {
|
||||
String testName = m.getName();
|
||||
if (testName.startsWith("test")) {
|
||||
count++;
|
||||
} else {
|
||||
throw new InternalError("wrong test name: " + testName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* =========================================================== */
|
||||
|
||||
interface TestCase {
|
||||
void run(A o, boolean cond);
|
||||
}
|
||||
|
||||
static void runTestCase(TestCase t) {
|
||||
A[] receivers = new A[] { new B() {}, new B() {}, new B() {}, new C() {}, new C() {}};
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
// Pollute type profile and branch frequencies.
|
||||
A recv = receivers[i % receivers.length];
|
||||
boolean cond = (i % 2 == 0);
|
||||
t.run(recv, cond);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,13 +132,13 @@ public class CastNullCheckDroppingsTest {
|
||||
t.runTest(methodClassCastNull, false, svalue);
|
||||
t.runTest(methodNullClassCast, false, svalue);
|
||||
t.runTest(methodClassCastObj, false, svalue);
|
||||
t.runTest(methodObjClassCast, false, svalue);
|
||||
t.runTest(methodObjClassCast, false, svalue);
|
||||
t.runTest(methodClassCastInt, false, svalue);
|
||||
t.runTest(methodIntClassCast, true, svalue);
|
||||
t.runTest(methodIntClassCast, false, svalue);
|
||||
t.runTest(methodClassCastint, false, svalue);
|
||||
t.runTest(methodintClassCast, false, svalue);
|
||||
t.runTest(methodClassCastPrim, false, svalue);
|
||||
t.runTest(methodPrimClassCast, true, svalue);
|
||||
t.runTest(methodPrimClassCast, false, svalue);
|
||||
t.runTest(methodVarClassCast, true, objClass);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user