Skip to content

Commit f2e56e4

Browse files
author
Vladimir Ivanov
committed
8372634: C2: Materialize type information from instanceof checks
Reviewed-by: dlong, qamai, roland
1 parent 4e9525e commit f2e56e4

File tree

10 files changed

+609
-34
lines changed

10 files changed

+609
-34
lines changed

src/hotspot/share/compiler/compilerDirectives.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,20 @@ bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
561561
return false;
562562
}
563563

564+
bool DirectiveSet::should_delay_inline(ciMethod* inlinee) {
565+
inlinee->check_is_loaded();
566+
VM_ENTRY_MARK;
567+
methodHandle mh(THREAD, inlinee->get_Method());
568+
569+
if (_inlinematchers != nullptr) {
570+
return matches_inline(mh, InlineMatcher::delay_inline);
571+
}
572+
if (!CompilerDirectivesIgnoreCompileCommandsOption) {
573+
return CompilerOracle::should_delay_inline(mh);
574+
}
575+
return false;
576+
}
577+
564578
bool DirectiveSet::parse_and_add_inline(char* str, const char*& error_msg) {
565579
InlineMatcher* m = InlineMatcher::parse_inline_pattern(str, error_msg);
566580
if (m != nullptr) {

src/hotspot/share/compiler/compilerDirectives.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class DirectiveSet : public CHeapObj<mtCompiler> {
142142
void append_inline(InlineMatcher* m);
143143
bool should_inline(ciMethod* inlinee);
144144
bool should_not_inline(ciMethod* inlinee);
145+
bool should_delay_inline(ciMethod* inlinee);
145146
void print_inline(outputStream* st);
146147
DirectiveSet* compilecommand_compatibility_init(const methodHandle& method);
147148
bool is_exclusive_copy() { return _directive == nullptr; }

src/hotspot/share/compiler/compilerOracle.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ bool CompilerOracle::should_not_inline(const methodHandle& method) {
480480
return check_predicate(CompileCommandEnum::DontInline, method) || check_predicate(CompileCommandEnum::Exclude, method);
481481
}
482482

483+
bool CompilerOracle::should_delay_inline(const methodHandle& method) {
484+
return (check_predicate(CompileCommandEnum::DelayInline, method));
485+
}
486+
483487
bool CompilerOracle::should_print(const methodHandle& method) {
484488
return check_predicate(CompileCommandEnum::Print, method);
485489
}

src/hotspot/share/compiler/compilerOracle.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class methodHandle;
5151
option(Log, "log", Bool) \
5252
option(Print, "print", Bool) \
5353
option(Inline, "inline", Bool) \
54+
option(DelayInline, "delayinline", Bool) \
5455
option(DontInline, "dontinline", Bool) \
5556
option(Blackhole, "blackhole", Bool) \
5657
option(CompileOnly, "compileonly", Bool)\
@@ -150,6 +151,9 @@ class CompilerOracle : AllStatic {
150151
// Tells whether we want to disallow inlining of this method
151152
static bool should_not_inline(const methodHandle& method);
152153

154+
// Tells whether we want to delay inlining of this method
155+
static bool should_delay_inline(const methodHandle& method);
156+
153157
// Tells whether this method changes Thread.currentThread()
154158
static bool changes_current_thread(const methodHandle& method);
155159

src/hotspot/share/compiler/methodMatcher.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class InlineMatcher : public MethodMatcher {
100100
enum InlineType {
101101
unknown_inline,
102102
dont_inline,
103+
delay_inline,
103104
force_inline
104105
};
105106

src/hotspot/share/opto/compile.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,8 @@ class Compile : public Phase {
984984
JVMState* jvms, bool allow_inline, float profile_factor, ciKlass* speculative_receiver_type = nullptr,
985985
bool allow_intrinsics = true);
986986
bool should_delay_inlining(ciMethod* call_method, JVMState* jvms) {
987-
return should_delay_string_inlining(call_method, jvms) ||
987+
return C->directive()->should_delay_inline(call_method) ||
988+
should_delay_string_inlining(call_method, jvms) ||
988989
should_delay_boxing_inlining(call_method, jvms) ||
989990
should_delay_vector_inlining(call_method, jvms);
990991
}

src/hotspot/share/opto/doCall.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
192192
// Try inlining a bytecoded method:
193193
if (!call_does_dispatch) {
194194
InlineTree* ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method());
195-
bool should_delay = C->should_delay_inlining();
195+
bool should_delay = C->should_delay_inlining() || C->directive()->should_delay_inline(callee);
196196
if (ilt->ok_to_inline(callee, jvms, profile, should_delay)) {
197197
CallGenerator* cg = CallGenerator::for_inline(callee, expected_uses);
198198
// For optimized virtual calls assert at runtime that receiver object

src/hotspot/share/opto/parse2.cpp

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "opto/opaquenode.hpp"
4242
#include "opto/parse.hpp"
4343
#include "opto/runtime.hpp"
44+
#include "opto/subtypenode.hpp"
4445
#include "runtime/deoptimization.hpp"
4546
#include "runtime/sharedRuntime.hpp"
4647

@@ -1719,38 +1720,108 @@ static Node* extract_obj_from_klass_load(PhaseGVN* gvn, Node* n) {
17191720
return obj;
17201721
}
17211722

1723+
// Matches exact and inexact type check IR shapes during parsing.
1724+
// On successful match, returns type checked object node and its type after successful check
1725+
// as out parameters.
1726+
static bool match_type_check(PhaseGVN& gvn,
1727+
BoolTest::mask btest,
1728+
Node* con, const Type* tcon,
1729+
Node* val, const Type* tval,
1730+
Node** obj, const TypeOopPtr** cast_type) { // out-parameters
1731+
// Look for opportunities to sharpen the type of a node whose klass is compared with a constant klass.
1732+
// The constant klass being tested against can come from many bytecode instructions (implicitly or explicitly),
1733+
// and also from profile data used by speculative casts.
1734+
if (btest == BoolTest::eq && tcon->isa_klassptr()) {
1735+
// Found:
1736+
// Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq])
1737+
// or the narrowOop equivalent.
1738+
(*obj) = extract_obj_from_klass_load(&gvn, val);
1739+
(*cast_type) = tcon->isa_klassptr()->as_instance_type();
1740+
return true; // found
1741+
}
1742+
1743+
// Match an instanceof check.
1744+
// During parsing its IR shape is not canonicalized yet.
1745+
//
1746+
// obj superklass
1747+
// | |
1748+
// SubTypeCheck
1749+
// |
1750+
// Bool [eq] / [ne]
1751+
// |
1752+
// If
1753+
// / \
1754+
// T F
1755+
// \ /
1756+
// Region
1757+
// \ ConI ConI
1758+
// \ | /
1759+
// val -> Phi ConI <- con
1760+
// \ /
1761+
// CmpI
1762+
// |
1763+
// Bool [btest]
1764+
// |
1765+
//
1766+
if (tval->isa_int() && val->is_Phi() && val->in(0)->as_Region()->is_diamond()) {
1767+
RegionNode* diamond = val->in(0)->as_Region();
1768+
IfNode* if1 = diamond->in(1)->in(0)->as_If();
1769+
BoolNode* b1 = if1->in(1)->isa_Bool();
1770+
if (b1 != nullptr && b1->in(1)->isa_SubTypeCheck()) {
1771+
assert(b1->_test._test == BoolTest::eq ||
1772+
b1->_test._test == BoolTest::ne, "%d", b1->_test._test);
1773+
1774+
ProjNode* success_proj = if1->proj_out(b1->_test._test == BoolTest::eq ? 1 : 0);
1775+
int idx = diamond->find_edge(success_proj);
1776+
assert(idx == 1 || idx == 2, "");
1777+
Node* vcon = val->in(idx);
1778+
1779+
assert(val->find_edge(con) > 0, "");
1780+
if ((btest == BoolTest::eq && vcon == con) || (btest == BoolTest::ne && vcon != con)) {
1781+
SubTypeCheckNode* sub = b1->in(1)->as_SubTypeCheck();
1782+
Node* obj_or_subklass = sub->in(SubTypeCheckNode::ObjOrSubKlass);
1783+
Node* superklass = sub->in(SubTypeCheckNode::SuperKlass);
1784+
1785+
if (gvn.type(obj_or_subklass)->isa_oopptr()) {
1786+
const TypeKlassPtr* klass_ptr_type = gvn.type(superklass)->is_klassptr();
1787+
const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve();
1788+
1789+
(*obj) = obj_or_subklass;
1790+
(*cast_type) = improved_klass_ptr_type->cast_to_exactness(false)->as_instance_type();
1791+
return true; // found
1792+
}
1793+
}
1794+
}
1795+
}
1796+
return false; // not found
1797+
}
1798+
17221799
void Parse::sharpen_type_after_if(BoolTest::mask btest,
17231800
Node* con, const Type* tcon,
17241801
Node* val, const Type* tval) {
1725-
// Look for opportunities to sharpen the type of a node
1726-
// whose klass is compared with a constant klass.
1727-
if (btest == BoolTest::eq && tcon->isa_klassptr()) {
1728-
Node* obj = extract_obj_from_klass_load(&_gvn, val);
1729-
const TypeOopPtr* con_type = tcon->isa_klassptr()->as_instance_type();
1730-
if (obj != nullptr && (con_type->isa_instptr() || con_type->isa_aryptr())) {
1731-
// Found:
1732-
// Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq])
1733-
// or the narrowOop equivalent.
1734-
const Type* obj_type = _gvn.type(obj);
1735-
const TypeOopPtr* tboth = obj_type->join_speculative(con_type)->isa_oopptr();
1736-
if (tboth != nullptr && tboth->klass_is_exact() && tboth != obj_type &&
1737-
tboth->higher_equal(obj_type)) {
1738-
// obj has to be of the exact type Foo if the CmpP succeeds.
1739-
int obj_in_map = map()->find_edge(obj);
1740-
JVMState* jvms = this->jvms();
1741-
if (obj_in_map >= 0 &&
1742-
(jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
1743-
TypeNode* ccast = new CheckCastPPNode(control(), obj, tboth);
1744-
const Type* tcc = ccast->as_Type()->type();
1745-
assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
1746-
// Delay transform() call to allow recovery of pre-cast value
1747-
// at the control merge.
1748-
_gvn.set_type_bottom(ccast);
1749-
record_for_igvn(ccast);
1750-
// Here's the payoff.
1751-
replace_in_map(obj, ccast);
1752-
}
1753-
}
1802+
Node* obj = nullptr;
1803+
const TypeOopPtr* cast_type = nullptr;
1804+
// Insert a cast node with a narrowed type after a successful type check.
1805+
if (match_type_check(_gvn, btest, con, tcon, val, tval,
1806+
&obj, &cast_type)) {
1807+
assert(obj != nullptr && cast_type != nullptr, "missing type check info");
1808+
const Type* obj_type = _gvn.type(obj);
1809+
const TypeOopPtr* tboth = obj_type->join_speculative(cast_type)->isa_oopptr();
1810+
if (tboth != nullptr && tboth != obj_type && tboth->higher_equal(obj_type)) {
1811+
int obj_in_map = map()->find_edge(obj);
1812+
JVMState* jvms = this->jvms();
1813+
if (obj_in_map >= 0 &&
1814+
(jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
1815+
TypeNode* ccast = new CheckCastPPNode(control(), obj, tboth);
1816+
const Type* tcc = ccast->as_Type()->type();
1817+
assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
1818+
// Delay transform() call to allow recovery of pre-cast value
1819+
// at the control merge.
1820+
_gvn.set_type_bottom(ccast);
1821+
record_for_igvn(ccast);
1822+
// Here's the payoff.
1823+
replace_in_map(obj, ccast);
1824+
}
17541825
}
17551826
}
17561827

0 commit comments

Comments
 (0)