8136725: Provide utility for creation a counted loop reserve copy (clone)

Make it easier to revert to the original loop should that be needed

Reviewed-by: kvn
This commit is contained in:
Jan Civlin 2015-10-09 12:17:58 -07:00 committed by Igor Veresov
parent 4c20e17d0a
commit 115afda88e
5 changed files with 253 additions and 1 deletions

View File

@ -342,6 +342,9 @@
product(bool, SuperWordReductions, true, \
"Enable reductions support in superword.") \
\
product(bool, DoReserveCopyInSuperWord, true, \
"Create reserve copy of graph in SuperWord.") \
\
notproduct(bool, TraceSuperWord, false, \
"Trace superword transforms") \
\

View File

@ -263,3 +263,136 @@ ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop,
return iffast;
}
LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, CountedLoopReserveKit* lk) {
Node_List old_new;
LoopNode* head = loop->_head->as_Loop();
bool counted_loop = head->is_CountedLoop();
Node* entry = head->in(LoopNode::EntryControl);
_igvn.rehash_node_delayed(entry);
IdealLoopTree* outer_loop = loop->_parent;
ConINode* const_1 = _igvn.intcon(1);
set_ctrl(const_1, C->root());
IfNode* iff = new IfNode(entry, const_1, PROB_MAX, COUNT_UNKNOWN);
register_node(iff, outer_loop, entry, dom_depth(entry));
ProjNode* iffast = new IfTrueNode(iff);
register_node(iffast, outer_loop, iff, dom_depth(iff));
ProjNode* ifslow = new IfFalseNode(iff);
register_node(ifslow, outer_loop, iff, dom_depth(iff));
// Clone the loop body. The clone becomes the fast loop. The
// original pre-header will (illegally) have 3 control users
// (old & new loops & new if).
clone_loop(loop, old_new, dom_depth(head), iff);
assert(old_new[head->_idx]->is_Loop(), "" );
LoopNode* slow_head = old_new[head->_idx]->as_Loop();
#ifndef PRODUCT
if (TraceLoopOpts) {
tty->print_cr("PhaseIdealLoop::create_reserve_version_of_loop:");
tty->print("\t iff = %d, ", iff->_idx); iff->dump();
tty->print("\t iffast = %d, ", iffast->_idx); iffast->dump();
tty->print("\t ifslow = %d, ", ifslow->_idx); ifslow->dump();
tty->print("\t before replace_input_of: head = %d, ", head->_idx); head->dump();
tty->print("\t before replace_input_of: slow_head = %d, ", slow_head->_idx); slow_head->dump();
}
#endif
// Fast (true) control
_igvn.replace_input_of(head, LoopNode::EntryControl, iffast);
// Slow (false) control
_igvn.replace_input_of(slow_head, LoopNode::EntryControl, ifslow);
recompute_dom_depth();
lk->set_iff(iff);
#ifndef PRODUCT
if (TraceLoopOpts ) {
tty->print("\t after replace_input_of: head = %d, ", head->_idx); head->dump();
tty->print("\t after replace_input_of: slow_head = %d, ", slow_head->_idx); slow_head->dump();
}
#endif
return slow_head->as_Loop();
}
CountedLoopReserveKit::CountedLoopReserveKit(PhaseIdealLoop* phase, IdealLoopTree *loop, bool active = true) :
_phase(phase),
_lpt(loop),
_lp(NULL),
_iff(NULL),
_lp_reserved(NULL),
_has_reserved(false),
_use_new(false),
_active(active)
{
create_reserve();
};
CountedLoopReserveKit::~CountedLoopReserveKit() {
if (!_active) {
return;
}
if (_has_reserved && !_use_new) {
// intcon(0)->iff-node reverts CF to the reserved copy
ConINode* const_0 = _phase->_igvn.intcon(0);
_phase->set_ctrl(const_0, _phase->C->root());
_iff->set_req(1, const_0);
#ifndef PRODUCT
if (TraceLoopOpts) {
tty->print_cr("CountedLoopReserveKit::~CountedLoopReserveKit()");
tty->print("\t discard loop %d and revert to the reserved loop clone %d: ", _lp->_idx, _lp_reserved->_idx);
_lp_reserved->dump();
}
#endif
}
}
bool CountedLoopReserveKit::create_reserve() {
if (!_active) {
return false;
}
if(!_lpt->_head->is_CountedLoop()) {
NOT_PRODUCT(if(TraceLoopOpts) {tty->print_cr("CountedLoopReserveKit::create_reserve: %d not counted loop", _lpt->_head->_idx);})
return false;
}
CountedLoopNode *cl = _lpt->_head->as_CountedLoop();
if (!cl->is_valid_counted_loop()) {
NOT_PRODUCT(if(TraceLoopOpts) {tty->print_cr("CountedLoopReserveKit::create_reserve: %d not valid counted loop", cl->_idx);})
return false; // skip malformed counted loop
}
if (!cl->is_main_loop()) {
NOT_PRODUCT(if(TraceLoopOpts) {tty->print_cr("CountedLoopReserveKit::create_reserve: %d not main loop", cl->_idx);})
return false; // skip normal, pre, and post loops
}
_lp = _lpt->_head->as_Loop();
_lp_reserved = _phase->create_reserve_version_of_loop(_lpt, this);
if (!_lp_reserved->is_CountedLoop()) {
return false;
}
Node* ifslow_pred = _lp_reserved->as_CountedLoop()->in(LoopNode::EntryControl);
if (!ifslow_pred->is_IfFalse()) {
return false;
}
Node* iff = ifslow_pred->in(0);
if (!iff->is_If() || iff != _iff) {
return false;
}
if (iff->in(1)->Opcode() != Op_ConI) {
return false;
}
return _has_reserved = true;
}

View File

@ -38,6 +38,7 @@ class IdealLoopTree;
class LoopNode;
class Node;
class PhaseIdealLoop;
class CountedLoopReserveKit;
class VectorSet;
class Invariance;
struct small_cache;
@ -529,6 +530,8 @@ public:
class PhaseIdealLoop : public PhaseTransform {
friend class IdealLoopTree;
friend class SuperWord;
friend class CountedLoopReserveKit;
// Pre-computed def-use info
PhaseIterGVN &_igvn;
@ -965,6 +968,16 @@ public:
ProjNode* create_slow_version_of_loop(IdealLoopTree *loop,
Node_List &old_new);
// Clone a loop and return the clone head (clone_loop_head).
// Added nodes include int(1), int(0) - disconnected, If, IfTrue, IfFalse,
// This routine was created for usage in CountedLoopReserveKit.
//
// int(1) -> If -> IfTrue -> original_loop_head
// |
// V
// IfFalse -> clone_loop_head (returned by function pointer)
//
LoopNode* create_reserve_version_of_loop(IdealLoopTree *loop, CountedLoopReserveKit* lk);
// Clone loop with an invariant test (that does not exit) and
// insert a clone of the test that selects which version to
// execute.
@ -1117,6 +1130,68 @@ public:
#endif
};
// This kit may be used for making of a reserved copy of a loop before this loop
// goes under non-reversible changes.
//
// Function create_reserve() creates a reserved copy (clone) of the loop.
// The reserved copy is created by calling
// PhaseIdealLoop::create_reserve_version_of_loop - see there how
// the original and reserved loops are connected in the outer graph.
// If create_reserve succeeded, it returns 'true' and _has_reserved is set to 'true'.
//
// By default the reserved copy (clone) of the loop is created as dead code - it is
// dominated in the outer loop by this node chain:
// intcon(1)->If->IfFalse->reserved_copy.
// The original loop is dominated by the the same node chain but IfTrue projection:
// intcon(1)->If->IfTrue->original_loop.
//
// In this implementation of CountedLoopReserveKit the ctor includes create_reserve()
// and the dtor, checks _use_new value.
// If _use_new == false, it "switches" control to reserved copy of the loop
// by simple replacing of node intcon(1) with node intcon(0).
//
// Here is a proposed example of usage (see also SuperWord::output in superword.cpp).
//
// void CountedLoopReserveKit_example()
// {
// CountedLoopReserveKit lrk((phase, lpt, DoReserveCopy = true); // create local object
// if (DoReserveCopy && !lrk.has_reserved()) {
// return; //failed to create reserved loop copy
// }
// ...
// //something is wrong, switch to original loop
/// if(something_is_wrong) return; // ~CountedLoopReserveKit makes the switch
// ...
// //everything worked ok, return with the newly modified loop
// lrk.use_new();
// return; // ~CountedLoopReserveKit does nothing once use_new() was called
// }
//
// Keep in mind, that by default if create_reserve() is not followed by use_new()
// the dtor will "switch to the original" loop.
// NOTE. You you modify outside of the original loop this class is no help.
//
class CountedLoopReserveKit {
private:
PhaseIdealLoop* _phase;
IdealLoopTree* _lpt;
LoopNode* _lp;
IfNode* _iff;
LoopNode* _lp_reserved;
bool _has_reserved;
bool _use_new;
const bool _active; //may be set to false in ctor, then the object is dummy
public:
CountedLoopReserveKit(PhaseIdealLoop* phase, IdealLoopTree *loop, bool active);
~CountedLoopReserveKit();
void use_new() {_use_new = true;}
void set_iff(IfNode* x) {_iff = x;}
bool has_reserved() const { return _active && _has_reserved;}
private:
bool create_reserve();
};// class CountedLoopReserveKit
inline Node* IdealLoopTree::tail() {
// Handle lazy update of _tail field
Node *n = _tail;

View File

@ -81,6 +81,10 @@ SuperWord::SuperWord(PhaseIdealLoop* phase) :
if (_phase->C->method() != NULL) {
_phase->C->method()->has_option_value("VectorizeDebug", _vector_loop_debug);
}
_CountedLoopReserveKit_debug = 0;
if (_phase->C->method() != NULL) {
_phase->C->method()->has_option_value("DoReserveCopyInSuperWordDebug", _CountedLoopReserveKit_debug);
}
#endif
}
@ -1763,6 +1767,22 @@ void SuperWord::co_locate_pack(Node_List* pk) {
}
}
#ifndef PRODUCT
void SuperWord::print_loop(bool whole) {
Node_Stack stack(_arena, _phase->C->unique() >> 2);
Node_List rpo_list;
VectorSet visited(_arena);
visited.set(lpt()->_head->_idx);
_phase->rpo(lpt()->_head, stack, visited, rpo_list);
_phase->dump(lpt(), rpo_list.size(), rpo_list );
if(whole) {
tty->print_cr("\n Whole loop tree");
_phase->dump();
tty->print_cr(" End of whole loop tree\n");
}
}
#endif
//------------------------------output---------------------------
// Convert packs into vector node operations
void SuperWord::output() {
@ -1770,7 +1790,7 @@ void SuperWord::output() {
#ifndef PRODUCT
if (TraceLoopOpts) {
tty->print("SuperWord ");
tty->print("SuperWord::output ");
lpt()->dump_head();
}
#endif
@ -1789,6 +1809,18 @@ void SuperWord::output() {
CountedLoopNode *cl = lpt()->_head->as_CountedLoop();
uint max_vlen_in_bytes = 0;
uint max_vlen = 0;
NOT_PRODUCT(if(_CountedLoopReserveKit_debug > 0) {tty->print_cr("SWPointer::output: print loop before create_reserve_version_of_loop"); print_loop(true);})
CountedLoopReserveKit make_reversable(_phase, _lpt, DoReserveCopyInSuperWord);
NOT_PRODUCT(if(_CountedLoopReserveKit_debug > 0) {tty->print_cr("SWPointer::output: print loop after create_reserve_version_of_loop"); print_loop(true);})
if (DoReserveCopyInSuperWord && !make_reversable.has_reserved()) {
NOT_PRODUCT({tty->print_cr("SWPointer::output: loop was not reserved correctly, exiting SuperWord");})
return;
}
for (int i = 0; i < _block.length(); i++) {
Node* n = _block.at(i);
Node_List* p = my_pack(n);
@ -1888,6 +1920,7 @@ void SuperWord::output() {
}
}
C->set_max_vector_size(max_vlen_in_bytes);
if (SuperWordLoopUnrollAnalysis) {
if (cl->has_passed_slp()) {
uint slp_max_unroll_factor = cl->slp_max_unroll();
@ -1900,6 +1933,12 @@ void SuperWord::output() {
}
}
}
if (DoReserveCopyInSuperWord) {
make_reversable.use_new();
}
NOT_PRODUCT(if(_CountedLoopReserveKit_debug > 0) {tty->print_cr("\n Final loop after SuperWord"); print_loop(true);})
return;
}
//------------------------------vector_opd---------------------------

View File

@ -299,6 +299,7 @@ class SuperWord : public ResourceObj {
GrowableArray<int> _ii_order;
#ifndef PRODUCT
uintx _vector_loop_debug; // provide more printing in debug mode
uintx _CountedLoopReserveKit_debug; // for debugging CountedLoopReserveKit
#endif
// Accessors
@ -375,6 +376,7 @@ class SuperWord : public ResourceObj {
// Tracing support
#ifndef PRODUCT
void find_adjacent_refs_trace_1(Node* best_align_to_mem_ref, int best_iv_adjustment);
void print_loop(bool whole);
#endif
// Find a memory reference to align the loop induction variable to.
MemNode* find_align_to_ref(Node_List &memops);