Skip to content

Commit 31e50f2

Browse files
author
Xin Liu
committed
8286104: use aggressive liveness for unstable_if traps
Reviewed-by: kvn, thartmann
1 parent dddd4e7 commit 31e50f2

14 files changed

+340
-10
lines changed

src/hotspot/share/compiler/methodLiveness.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class MethodLivenessResult : public ResourceBitMap {
4646
{}
4747

4848
void set_is_valid() { _is_valid = true; }
49-
bool is_valid() { return _is_valid; }
49+
bool is_valid() const { return _is_valid; }
5050
};
5151

5252
class MethodLiveness : public ResourceObj {

src/hotspot/share/opto/c2_globals.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@
416416
"Set level of loop optimization for tier 1 compiles") \
417417
range(5, 43) \
418418
\
419+
product(bool, OptimizeUnstableIf, true, DIAGNOSTIC, \
420+
"Optimize UnstableIf traps") \
421+
\
419422
/* controls for heat-based inlining */ \
420423
\
421424
develop(intx, NodeCountInliningCutoff, 18000, \

src/hotspot/share/opto/callnode.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,12 @@ Node *SafePointNode::peek_monitor_obj() const {
14591459
return monitor_obj(jvms(), mon);
14601460
}
14611461

1462+
Node* SafePointNode::peek_operand(uint off) const {
1463+
assert(jvms()->sp() > 0, "must have an operand");
1464+
assert(off < jvms()->sp(), "off is out-of-range");
1465+
return stack(jvms(), jvms()->sp() - off - 1);
1466+
}
1467+
14621468
// Do we Match on this edge index or not? Match no edges
14631469
uint SafePointNode::match_edge(uint idx) const {
14641470
return (TypeFunc::Parms == idx);

src/hotspot/share/opto/callnode.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,8 @@ class SafePointNode : public MultiNode {
416416
void pop_monitor ();
417417
Node *peek_monitor_box() const;
418418
Node *peek_monitor_obj() const;
419+
// Peek Operand Stacks, JVMS 2.6.2
420+
Node* peek_operand(uint off = 0) const;
419421

420422
// Access functions for the JVM
421423
Node *control () const { return in(TypeFunc::Control ); }

src/hotspot/share/opto/compile.cpp

+108
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,10 @@ void Compile::remove_useless_node(Node* dead) {
396396
remove_useless_late_inlines( &_string_late_inlines, dead);
397397
remove_useless_late_inlines( &_boxing_late_inlines, dead);
398398
remove_useless_late_inlines(&_vector_reboxing_late_inlines, dead);
399+
400+
if (dead->is_CallStaticJava()) {
401+
remove_unstable_if_trap(dead->as_CallStaticJava(), false);
402+
}
399403
}
400404
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
401405
bs->unregister_potential_barrier_node(dead);
@@ -434,6 +438,7 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
434438
remove_useless_nodes(_skeleton_predicate_opaqs, useful);
435439
remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes
436440
remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass
441+
remove_useless_unstable_if_traps(useful); // remove useless unstable_if traps
437442
remove_useless_coarsened_locks(useful); // remove useless coarsened locks nodes
438443

439444
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
@@ -607,6 +612,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
607612
_skeleton_predicate_opaqs (comp_arena(), 8, 0, NULL),
608613
_expensive_nodes (comp_arena(), 8, 0, NULL),
609614
_for_post_loop_igvn(comp_arena(), 8, 0, NULL),
615+
_unstable_if_traps (comp_arena(), 8, 0, NULL),
610616
_coarsened_locks (comp_arena(), 8, 0, NULL),
611617
_congraph(NULL),
612618
NOT_PRODUCT(_igv_printer(NULL) COMMA)
@@ -1854,6 +1860,106 @@ void Compile::process_for_post_loop_opts_igvn(PhaseIterGVN& igvn) {
18541860
}
18551861
}
18561862

1863+
void Compile::record_unstable_if_trap(UnstableIfTrap* trap) {
1864+
if (OptimizeUnstableIf) {
1865+
_unstable_if_traps.append(trap);
1866+
}
1867+
}
1868+
1869+
void Compile::remove_useless_unstable_if_traps(Unique_Node_List& useful) {
1870+
for (int i = _unstable_if_traps.length() - 1; i >= 0; i--) {
1871+
UnstableIfTrap* trap = _unstable_if_traps.at(i);
1872+
Node* n = trap->uncommon_trap();
1873+
if (!useful.member(n)) {
1874+
_unstable_if_traps.delete_at(i); // replaces i-th with last element which is known to be useful (already processed)
1875+
}
1876+
}
1877+
}
1878+
1879+
// Remove the unstable if trap associated with 'unc' from candidates. It is either dead
1880+
// or fold-compares case. Return true if succeed or not found.
1881+
//
1882+
// In rare cases, the found trap has been processed. It is too late to delete it. Return
1883+
// false and ask fold-compares to yield.
1884+
//
1885+
// 'fold-compares' may use the uncommon_trap of the dominating IfNode to cover the fused
1886+
// IfNode. This breaks the unstable_if trap invariant: control takes the unstable path
1887+
// when deoptimization does happen.
1888+
bool Compile::remove_unstable_if_trap(CallStaticJavaNode* unc, bool yield) {
1889+
for (int i = 0; i < _unstable_if_traps.length(); ++i) {
1890+
UnstableIfTrap* trap = _unstable_if_traps.at(i);
1891+
if (trap->uncommon_trap() == unc) {
1892+
if (yield && trap->modified()) {
1893+
return false;
1894+
}
1895+
_unstable_if_traps.delete_at(i);
1896+
break;
1897+
}
1898+
}
1899+
return true;
1900+
}
1901+
1902+
// Re-calculate unstable_if traps with the liveness of next_bci, which points to the unlikely path.
1903+
// It needs to be done after igvn because fold-compares may fuse uncommon_traps and before renumbering.
1904+
void Compile::process_for_unstable_if_traps(PhaseIterGVN& igvn) {
1905+
for (int i = _unstable_if_traps.length() - 1; i >= 0; --i) {
1906+
UnstableIfTrap* trap = _unstable_if_traps.at(i);
1907+
CallStaticJavaNode* unc = trap->uncommon_trap();
1908+
int next_bci = trap->next_bci();
1909+
bool modified = trap->modified();
1910+
1911+
if (next_bci != -1 && !modified) {
1912+
assert(!_dead_node_list.test(unc->_idx), "changing a dead node!");
1913+
JVMState* jvms = unc->jvms();
1914+
ciMethod* method = jvms->method();
1915+
ciBytecodeStream iter(method);
1916+
1917+
iter.force_bci(jvms->bci());
1918+
assert(next_bci == iter.next_bci() || next_bci == iter.get_dest(), "wrong next_bci at unstable_if");
1919+
Bytecodes::Code c = iter.cur_bc();
1920+
Node* lhs = nullptr;
1921+
Node* rhs = nullptr;
1922+
if (c == Bytecodes::_if_acmpeq || c == Bytecodes::_if_acmpne) {
1923+
lhs = unc->peek_operand(0);
1924+
rhs = unc->peek_operand(1);
1925+
} else if (c == Bytecodes::_ifnull || c == Bytecodes::_ifnonnull) {
1926+
lhs = unc->peek_operand(0);
1927+
}
1928+
1929+
ResourceMark rm;
1930+
const MethodLivenessResult& live_locals = method->liveness_at_bci(next_bci);
1931+
assert(live_locals.is_valid(), "broken liveness info");
1932+
int len = (int)live_locals.size();
1933+
1934+
for (int i = 0; i < len; i++) {
1935+
Node* local = unc->local(jvms, i);
1936+
// kill local using the liveness of next_bci.
1937+
// give up when the local looks like an operand to secure reexecution.
1938+
if (!live_locals.at(i) && !local->is_top() && local != lhs && local!= rhs) {
1939+
uint idx = jvms->locoff() + i;
1940+
#ifdef ASSERT
1941+
if (Verbose) {
1942+
tty->print("[unstable_if] kill local#%d: ", idx);
1943+
local->dump();
1944+
tty->cr();
1945+
}
1946+
#endif
1947+
igvn.replace_input_of(unc, idx, top());
1948+
modified = true;
1949+
}
1950+
}
1951+
}
1952+
1953+
// keep the mondified trap for late query
1954+
if (modified) {
1955+
trap->set_modified();
1956+
} else {
1957+
_unstable_if_traps.delete_at(i);
1958+
}
1959+
}
1960+
igvn.optimize();
1961+
}
1962+
18571963
// StringOpts and late inlining of string methods
18581964
void Compile::inline_string_calls(bool parse_time) {
18591965
{
@@ -2138,6 +2244,8 @@ void Compile::Optimize() {
21382244

21392245
print_method(PHASE_ITER_GVN1, 2);
21402246

2247+
process_for_unstable_if_traps(igvn);
2248+
21412249
inline_incrementally(igvn);
21422250

21432251
print_method(PHASE_INCREMENTAL_INLINE, 2);

src/hotspot/share/opto/compile.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class AddPNode;
5151
class Block;
5252
class Bundle;
5353
class CallGenerator;
54+
class CallStaticJavaNode;
5455
class CloneMap;
5556
class ConnectionGraph;
5657
class IdealGraphPrinter;
@@ -90,6 +91,7 @@ class TypeOopPtr;
9091
class TypeFunc;
9192
class TypeVect;
9293
class Unique_Node_List;
94+
class UnstableIfTrap;
9395
class nmethod;
9496
class Node_Stack;
9597
struct Final_Reshape_Counts;
@@ -357,6 +359,7 @@ class Compile : public Phase {
357359
GrowableArray<Node*> _skeleton_predicate_opaqs; // List of Opaque4 nodes for the loop skeleton predicates.
358360
GrowableArray<Node*> _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common
359361
GrowableArray<Node*> _for_post_loop_igvn; // List of nodes for IGVN after loop opts are over
362+
GrowableArray<UnstableIfTrap*> _unstable_if_traps; // List of ifnodes after IGVN
360363
GrowableArray<Node_List*> _coarsened_locks; // List of coarsened Lock and Unlock nodes
361364
ConnectionGraph* _congraph;
362365
#ifndef PRODUCT
@@ -732,6 +735,11 @@ class Compile : public Phase {
732735
void remove_from_post_loop_opts_igvn(Node* n);
733736
void process_for_post_loop_opts_igvn(PhaseIterGVN& igvn);
734737

738+
void record_unstable_if_trap(UnstableIfTrap* trap);
739+
bool remove_unstable_if_trap(CallStaticJavaNode* unc, bool yield);
740+
void remove_useless_unstable_if_traps(Unique_Node_List &useful);
741+
void process_for_unstable_if_traps(PhaseIterGVN& igvn);
742+
735743
void sort_macro_nodes();
736744

737745
// remove the opaque nodes that protect the predicates so that the unused checks and

src/hotspot/share/opto/graphKit.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -2018,12 +2018,12 @@ void GraphKit::increment_counter(Node* counter_addr) {
20182018
// Bail out to the interpreter in mid-method. Implemented by calling the
20192019
// uncommon_trap blob. This helper function inserts a runtime call with the
20202020
// right debug info.
2021-
void GraphKit::uncommon_trap(int trap_request,
2021+
Node* GraphKit::uncommon_trap(int trap_request,
20222022
ciKlass* klass, const char* comment,
20232023
bool must_throw,
20242024
bool keep_exact_action) {
20252025
if (failing()) stop();
2026-
if (stopped()) return; // trap reachable?
2026+
if (stopped()) return NULL; // trap reachable?
20272027

20282028
// Note: If ProfileTraps is true, and if a deopt. actually
20292029
// occurs here, the runtime will make sure an MDO exists. There is
@@ -2139,6 +2139,7 @@ void GraphKit::uncommon_trap(int trap_request,
21392139
root()->add_req(halt);
21402140

21412141
stop_and_kill_map();
2142+
return call;
21422143
}
21432144

21442145

src/hotspot/share/opto/graphKit.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -728,25 +728,25 @@ class GraphKit : public Phase {
728728
// The optional klass is the one causing the trap.
729729
// The optional reason is debug information written to the compile log.
730730
// Optional must_throw is the same as with add_safepoint_edges.
731-
void uncommon_trap(int trap_request,
731+
Node* uncommon_trap(int trap_request,
732732
ciKlass* klass = NULL, const char* reason_string = NULL,
733733
bool must_throw = false, bool keep_exact_action = false);
734734

735735
// Shorthand, to avoid saying "Deoptimization::" so many times.
736-
void uncommon_trap(Deoptimization::DeoptReason reason,
736+
Node* uncommon_trap(Deoptimization::DeoptReason reason,
737737
Deoptimization::DeoptAction action,
738738
ciKlass* klass = NULL, const char* reason_string = NULL,
739739
bool must_throw = false, bool keep_exact_action = false) {
740-
uncommon_trap(Deoptimization::make_trap_request(reason, action),
740+
return uncommon_trap(Deoptimization::make_trap_request(reason, action),
741741
klass, reason_string, must_throw, keep_exact_action);
742742
}
743743

744744
// Bail out to the interpreter and keep exact action (avoid switching to Action_none).
745-
void uncommon_trap_exact(Deoptimization::DeoptReason reason,
745+
Node* uncommon_trap_exact(Deoptimization::DeoptReason reason,
746746
Deoptimization::DeoptAction action,
747747
ciKlass* klass = NULL, const char* reason_string = NULL,
748748
bool must_throw = false) {
749-
uncommon_trap(Deoptimization::make_trap_request(reason, action),
749+
return uncommon_trap(Deoptimization::make_trap_request(reason, action),
750750
klass, reason_string, must_throw, /*keep_exact_action=*/true);
751751
}
752752

src/hotspot/share/opto/ifnode.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,9 @@ bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNod
838838
ciMethod* dom_method = dom_unc->jvms()->method();
839839
int dom_bci = dom_unc->jvms()->bci();
840840
if (!igvn->C->too_many_traps(dom_method, dom_bci, Deoptimization::Reason_unstable_fused_if) &&
841-
!igvn->C->too_many_traps(dom_method, dom_bci, Deoptimization::Reason_range_check)) {
841+
!igvn->C->too_many_traps(dom_method, dom_bci, Deoptimization::Reason_range_check) &&
842+
// Return true if c2 manages to reconcile with UnstableIf optimization. See the comments for it.
843+
igvn->C->remove_unstable_if_trap(dom_unc, true/*yield*/)) {
842844
success = unc_proj;
843845
fail = unc_proj->other_if_proj();
844846
return true;

src/hotspot/share/opto/node.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,10 @@ void Node::destruct(PhaseValues* phase) {
665665

666666
if (is_SafePoint()) {
667667
as_SafePoint()->delete_replaced_nodes();
668+
669+
if (is_CallStaticJava()) {
670+
compile->remove_unstable_if_trap(as_CallStaticJava(), false);
671+
}
668672
}
669673
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
670674
bs->unregister_potential_barrier_node(this);

src/hotspot/share/opto/parse.hpp

+37
Original file line numberDiff line numberDiff line change
@@ -603,4 +603,41 @@ class Parse : public GraphKit {
603603
#endif
604604
};
605605

606+
// Specialized uncommon_trap of unstable_if. C2 uses next_bci of path to update the live locals of it.
607+
class UnstableIfTrap {
608+
CallStaticJavaNode* const _unc;
609+
bool _modified; // modified locals based on next_bci()
610+
int _next_bci;
611+
612+
public:
613+
UnstableIfTrap(CallStaticJavaNode* call, Parse::Block* path): _unc(call), _modified(false) {
614+
assert(_unc != NULL && Deoptimization::trap_request_reason(_unc->uncommon_trap_request()) == Deoptimization::Reason_unstable_if,
615+
"invalid uncommon_trap call!");
616+
_next_bci = path != nullptr ? path->start() : -1;
617+
}
618+
619+
// The starting point of the pruned block, where control goes when
620+
// deoptimization does happen.
621+
int next_bci() const {
622+
return _next_bci;
623+
}
624+
625+
bool modified() const {
626+
return _modified;
627+
}
628+
629+
void set_modified() {
630+
_modified = true;
631+
}
632+
633+
CallStaticJavaNode* uncommon_trap() const {
634+
return _unc;
635+
}
636+
637+
inline void* operator new(size_t x) throw() {
638+
Compile* C = Compile::current();
639+
return C->comp_arena()->AmallocWords(x);
640+
}
641+
};
642+
606643
#endif // SHARE_OPTO_PARSE_HPP

src/hotspot/share/opto/parse2.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1586,10 +1586,14 @@ void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
15861586

15871587
if (path_is_suitable_for_uncommon_trap(prob)) {
15881588
repush_if_args();
1589-
uncommon_trap(Deoptimization::Reason_unstable_if,
1589+
Node* call = uncommon_trap(Deoptimization::Reason_unstable_if,
15901590
Deoptimization::Action_reinterpret,
15911591
NULL,
15921592
(is_fallthrough ? "taken always" : "taken never"));
1593+
1594+
if (call != nullptr) {
1595+
C->record_unstable_if_trap(new UnstableIfTrap(call->as_CallStaticJava(), path));
1596+
}
15931597
return;
15941598
}
15951599

0 commit comments

Comments
 (0)