8342047: Create Template Assertion Predicates with Halt nodes only instead of uncommon traps
Reviewed-by: epeter, thartmann
This commit is contained in:
parent
23a8c71d3b
commit
c977ef7b45
@ -283,62 +283,57 @@ IfProjNode* PhaseIdealLoop::clone_parse_predicate_to_unswitched_loop(ParsePredic
|
||||
return new_predicate_proj;
|
||||
}
|
||||
|
||||
// Clones Assertion Predicates to both unswitched loops starting at 'old_predicate_proj' by following its control inputs.
|
||||
// It also rewires the control edges of data nodes with dependencies in the loop from the old predicates to the new
|
||||
// cloned predicates.
|
||||
// Clones Template Assertion Predicates to both unswitched loops starting at 'old_predicate_proj' by following its
|
||||
// control inputs. It also rewires the control edges of data nodes with dependencies in the loop from the old predicates
|
||||
// to the new cloned predicates.
|
||||
void PhaseIdealLoop::clone_assertion_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new,
|
||||
Deoptimization::DeoptReason reason,
|
||||
ParsePredicateSuccessProj* old_parse_predicate_proj,
|
||||
ParsePredicateSuccessProj* fast_loop_parse_predicate_proj,
|
||||
ParsePredicateSuccessProj* slow_loop_parse_predicate_proj) {
|
||||
assert(fast_loop_parse_predicate_proj->in(0)->is_ParsePredicate() &&
|
||||
slow_loop_parse_predicate_proj->in(0)->is_ParsePredicate(), "sanity check");
|
||||
// Only need to clone range check predicates as those can be changed and duplicated by inserting pre/main/post loops
|
||||
// and doing loop unrolling. Push the original predicates on a list to later process them in reverse order to keep the
|
||||
ParsePredicateNode* true_path_loop_parse_predicate,
|
||||
ParsePredicateNode* false_path_loop_parse_predicate) {
|
||||
// Push the original Template Assertion Predicates on a list to later process them in reverse order to keep the
|
||||
// original predicate order.
|
||||
Unique_Node_List list;
|
||||
get_assertion_predicates(old_parse_predicate_proj, list);
|
||||
get_template_assertion_predicates(old_parse_predicate_proj, list);
|
||||
|
||||
Node_List to_process;
|
||||
// Process in reverse order such that 'create_new_if_for_predicate' can be used in
|
||||
// 'clone_assertion_predicate_for_unswitched_loops' and the original order is maintained.
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
Node* predicate = list.at(i);
|
||||
assert(predicate->in(0)->is_If(), "must be If node");
|
||||
IfNode* iff = predicate->in(0)->as_If();
|
||||
assert(predicate->is_Proj() && predicate->as_Proj()->is_IfProj(), "predicate must be a projection of an if node");
|
||||
IfProjNode* predicate_proj = predicate->as_IfProj();
|
||||
IfTrueNode* template_assertion_predicate_success_proj = list.at(i)->as_IfTrue();
|
||||
assert(template_assertion_predicate_success_proj->in(0)->is_If(), "must be If node");
|
||||
|
||||
IfProjNode* fast_proj = clone_assertion_predicate_for_unswitched_loops(iff, predicate_proj, reason, fast_loop_parse_predicate_proj);
|
||||
assert(assertion_predicate_has_loop_opaque_node(fast_proj->in(0)->as_If()), "must find Assertion Predicate for fast loop");
|
||||
IfProjNode* slow_proj = clone_assertion_predicate_for_unswitched_loops(iff, predicate_proj, reason, slow_loop_parse_predicate_proj);
|
||||
assert(assertion_predicate_has_loop_opaque_node(slow_proj->in(0)->as_If()), "must find Assertion Predicate for slow loop");
|
||||
IfTrueNode* true_path_loop_proj =
|
||||
clone_assertion_predicate_for_unswitched_loops(template_assertion_predicate_success_proj,
|
||||
true_path_loop_parse_predicate);
|
||||
IfTrueNode* false_path_loop_proj =
|
||||
clone_assertion_predicate_for_unswitched_loops(template_assertion_predicate_success_proj,
|
||||
false_path_loop_parse_predicate);
|
||||
|
||||
// Update control dependent data nodes.
|
||||
for (DUIterator j = predicate->outs(); predicate->has_out(j); j++) {
|
||||
Node* fast_node = predicate->out(j);
|
||||
if (loop->is_member(get_loop(ctrl_or_self(fast_node)))) {
|
||||
assert(fast_node->in(0) == predicate, "only control edge");
|
||||
Node* slow_node = old_new[fast_node->_idx];
|
||||
assert(slow_node->in(0) == predicate, "only control edge");
|
||||
_igvn.replace_input_of(fast_node, 0, fast_proj);
|
||||
to_process.push(slow_node);
|
||||
for (DUIterator j = template_assertion_predicate_success_proj->outs();
|
||||
template_assertion_predicate_success_proj->has_out(j);
|
||||
j++) {
|
||||
Node* true_path_loop_node = template_assertion_predicate_success_proj->out(j);
|
||||
if (loop->is_member(get_loop(ctrl_or_self(true_path_loop_node)))) {
|
||||
assert(true_path_loop_node->in(0) == template_assertion_predicate_success_proj, "only control edge");
|
||||
Node* false_path_loop_node = old_new[true_path_loop_node->_idx];
|
||||
assert(false_path_loop_node->in(0) == template_assertion_predicate_success_proj, "only control edge");
|
||||
_igvn.replace_input_of(true_path_loop_node, 0, true_path_loop_proj);
|
||||
to_process.push(false_path_loop_node);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
// Have to delay updates to the slow loop so uses of predicate are not modified while we iterate on them.
|
||||
// Have to delay updates to the false path loop so uses of predicate are not modified while we iterate on them.
|
||||
while (to_process.size() > 0) {
|
||||
Node* slow_node = to_process.pop();
|
||||
_igvn.replace_input_of(slow_node, 0, slow_proj);
|
||||
_igvn.replace_input_of(slow_node, 0, false_path_loop_proj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put all Assertion Predicate projections on a list, starting at 'predicate' and going up in the tree. If 'get_opaque'
|
||||
// Put all Template Assertion Predicate projections on a list, starting at 'predicate' and going up in the tree. If 'get_opaque'
|
||||
// is set, then the OpaqueTemplateAssertionPredicateNode nodes of the Assertion Predicates are put on the list instead
|
||||
// of the projections.
|
||||
void PhaseIdealLoop::get_assertion_predicates(ParsePredicateSuccessProj* parse_predicate_proj, Unique_Node_List& list,
|
||||
const bool get_opaque) {
|
||||
void PhaseIdealLoop::get_template_assertion_predicates(ParsePredicateSuccessProj* parse_predicate_proj, Unique_Node_List& list,
|
||||
const bool get_opaque) {
|
||||
Deoptimization::DeoptReason deopt_reason = parse_predicate_proj->in(0)->as_ParsePredicate()->deopt_reason();
|
||||
PredicateBlockIterator predicate_iterator(parse_predicate_proj, deopt_reason);
|
||||
TemplateAssertionPredicateCollector template_assertion_predicate_collector(list, get_opaque);
|
||||
@ -348,39 +343,38 @@ void PhaseIdealLoop::get_assertion_predicates(ParsePredicateSuccessProj* parse_p
|
||||
// Clone an Assertion Predicate for an unswitched loop. OpaqueLoopInit and OpaqueLoopStride nodes are cloned and uncommon
|
||||
// traps are kept for the predicate (a Halt node is used later when creating pre/main/post loops and copying this cloned
|
||||
// predicate again).
|
||||
IfProjNode* PhaseIdealLoop::clone_assertion_predicate_for_unswitched_loops(IfNode* template_assertion_predicate,
|
||||
IfProjNode* predicate,
|
||||
Deoptimization::DeoptReason reason,
|
||||
ParsePredicateSuccessProj* parse_predicate_proj) {
|
||||
TemplateAssertionExpression template_assertion_expression(template_assertion_predicate->in(1)->as_OpaqueTemplateAssertionPredicate());
|
||||
OpaqueTemplateAssertionPredicateNode* cloned_opaque_node = template_assertion_expression.clone(parse_predicate_proj->in(0)->in(0), this);
|
||||
IfProjNode* if_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason,
|
||||
template_assertion_predicate->Opcode(), false);
|
||||
_igvn.replace_input_of(if_proj->in(0), 1, cloned_opaque_node);
|
||||
_igvn.replace_input_of(parse_predicate_proj->in(0), 0, if_proj);
|
||||
set_idom(parse_predicate_proj->in(0), if_proj, dom_depth(if_proj));
|
||||
return if_proj;
|
||||
IfTrueNode*
|
||||
PhaseIdealLoop::clone_assertion_predicate_for_unswitched_loops(IfTrueNode* template_assertion_predicate_success_proj,
|
||||
ParsePredicateNode* unswitched_loop_parse_predicate) {
|
||||
TemplateAssertionPredicate template_assertion_predicate(template_assertion_predicate_success_proj);
|
||||
IfTrueNode* template_success_proj = template_assertion_predicate.clone(unswitched_loop_parse_predicate->in(0), this);
|
||||
assert(assertion_predicate_has_loop_opaque_node(template_success_proj->in(0)->as_If()),
|
||||
"must find Assertion Predicate for fast loop");
|
||||
_igvn.replace_input_of(unswitched_loop_parse_predicate, 0, template_success_proj);
|
||||
set_idom(unswitched_loop_parse_predicate, template_success_proj, dom_depth(template_success_proj));
|
||||
return template_success_proj;
|
||||
}
|
||||
|
||||
// Clone the old Parse Predicates and Assertion Predicates before the unswitch If to the unswitched loops after the
|
||||
// unswitch If.
|
||||
void PhaseIdealLoop::clone_parse_and_assertion_predicates_to_unswitched_loop(IdealLoopTree* loop, Node_List& old_new,
|
||||
IfProjNode*& iffast_pred, IfProjNode*& ifslow_pred) {
|
||||
IfProjNode*& true_path_loop_entry,
|
||||
IfProjNode*& false_path_loop_entry) {
|
||||
LoopNode* head = loop->_head->as_Loop();
|
||||
Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
|
||||
|
||||
const Predicates predicates(entry);
|
||||
clone_loop_predication_predicates_to_unswitched_loop(loop, old_new, predicates.loop_predicate_block(),
|
||||
Deoptimization::Reason_predicate, iffast_pred, ifslow_pred);
|
||||
Deoptimization::Reason_predicate, true_path_loop_entry, false_path_loop_entry);
|
||||
clone_loop_predication_predicates_to_unswitched_loop(loop, old_new, predicates.profiled_loop_predicate_block(),
|
||||
Deoptimization::Reason_profile_predicate, iffast_pred, ifslow_pred);
|
||||
Deoptimization::Reason_profile_predicate, true_path_loop_entry, false_path_loop_entry);
|
||||
|
||||
const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block();
|
||||
if (loop_limit_check_predicate_block->has_parse_predicate() && !head->is_CountedLoop()) {
|
||||
// Don't clone the Loop Limit Check Parse Predicate if we already have a counted loop (a Loop Limit Check Predicate
|
||||
// is only created when converting a LoopNode to a CountedLoopNode).
|
||||
clone_parse_predicate_to_unswitched_loops(loop_limit_check_predicate_block, Deoptimization::Reason_loop_limit_check,
|
||||
iffast_pred, ifslow_pred);
|
||||
true_path_loop_entry, false_path_loop_entry);
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,16 +382,17 @@ void PhaseIdealLoop::clone_parse_and_assertion_predicates_to_unswitched_loop(Ide
|
||||
void PhaseIdealLoop::clone_loop_predication_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new,
|
||||
const PredicateBlock* predicate_block,
|
||||
Deoptimization::DeoptReason reason,
|
||||
IfProjNode*& iffast_pred,
|
||||
IfProjNode*& ifslow_pred) {
|
||||
IfProjNode*& true_path_loop_entry,
|
||||
IfProjNode*& false_path_loop_entry) {
|
||||
if (predicate_block->has_parse_predicate()) {
|
||||
// We currently only clone Assertion Predicates if there are Parse Predicates. This is not entirely correct and will
|
||||
// be changed with the complete fix for Assertion Predicates.
|
||||
clone_parse_predicate_to_unswitched_loops(predicate_block, reason, iffast_pred, ifslow_pred);
|
||||
assert(iffast_pred->in(0)->is_ParsePredicate() && ifslow_pred->in(0)->is_ParsePredicate(),
|
||||
clone_parse_predicate_to_unswitched_loops(predicate_block, reason, true_path_loop_entry, false_path_loop_entry);
|
||||
assert(true_path_loop_entry->in(0)->is_ParsePredicate() && false_path_loop_entry->in(0)->is_ParsePredicate(),
|
||||
"must be success projections of the cloned Parse Predicates");
|
||||
clone_assertion_predicates_to_unswitched_loop(loop, old_new, reason, predicate_block->parse_predicate_success_proj(),
|
||||
iffast_pred->as_IfTrue(), ifslow_pred->as_IfTrue());
|
||||
clone_assertion_predicates_to_unswitched_loop(loop, old_new, predicate_block->parse_predicate_success_proj(),
|
||||
true_path_loop_entry->in(0)->as_ParsePredicate(),
|
||||
false_path_loop_entry->in(0)->as_ParsePredicate());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1250,9 +1245,9 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
|
||||
// Fall through into rest of the cleanup code which will move any dependent nodes to the skeleton predicates of the
|
||||
// upper bound test. We always need to create skeleton predicates in order to properly remove dead loops when later
|
||||
// splitting the predicated loop into (unreachable) sub-loops (i.e. done by unrolling, peeling, pre/main/post etc.).
|
||||
IfTrueNode* template_assertion_predicate_proj =
|
||||
create_template_assertion_predicate(if_opcode, cl, parse_predicate_proj, upper_bound_proj, scale, offset, range,
|
||||
deopt_reason);
|
||||
IfTrueNode* template_assertion_predicate_proj = create_template_assertion_predicate(cl, parse_predicate,
|
||||
upper_bound_proj, scale, offset,
|
||||
range);
|
||||
|
||||
// Eliminate the old range check in the loop body.
|
||||
// When a range check is eliminated, data dependent nodes (Load and range check CastII nodes) are now dependent on 2
|
||||
@ -1288,15 +1283,16 @@ void PhaseIdealLoop::eliminate_hoisted_range_check(IfTrueNode* hoisted_check_pro
|
||||
// Each newly created Hoisted Check Predicate is accompanied by two Template Assertion Predicates. Later, we initialize
|
||||
// them by making a copy of them when splitting a loop into sub loops. The Assertion Predicates ensure that dead sub
|
||||
// loops are removed properly.
|
||||
IfTrueNode* PhaseIdealLoop::create_template_assertion_predicate(const int if_opcode, CountedLoopNode* loop_head,
|
||||
ParsePredicateSuccessProj* parse_predicate_proj,
|
||||
IfTrueNode* PhaseIdealLoop::create_template_assertion_predicate(CountedLoopNode* loop_head,
|
||||
ParsePredicateNode* parse_predicate,
|
||||
IfProjNode* new_control, const int scale, Node* offset,
|
||||
Node* range, Deoptimization::DeoptReason deopt_reason) {
|
||||
Node* range) {
|
||||
|
||||
TemplateAssertionPredicateCreator template_assertion_predicate_creator(loop_head, scale, offset, range, this);
|
||||
return template_assertion_predicate_creator.create_with_uncommon_trap(new_control, parse_predicate_proj, deopt_reason,
|
||||
if_opcode);
|
||||
|
||||
IfTrueNode* template_success_proj = template_assertion_predicate_creator.create(new_control);
|
||||
_igvn.replace_input_of(parse_predicate, 0, template_success_proj);
|
||||
set_idom(parse_predicate, template_success_proj, dom_depth(template_success_proj));
|
||||
return template_success_proj;
|
||||
}
|
||||
|
||||
// Insert Hoisted Check Predicates for null checks and range checks and additional Template Assertion Predicates for
|
||||
|
@ -2768,7 +2768,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {
|
||||
// unrolling or splitting this main-loop further.
|
||||
TemplateAssertionPredicateCreator template_assertion_predicate_creator(cl, scale_con , int_offset, int_limit,
|
||||
this);
|
||||
loop_entry = template_assertion_predicate_creator.create_with_halt(loop_entry);
|
||||
loop_entry = template_assertion_predicate_creator.create(loop_entry);
|
||||
assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");
|
||||
|
||||
// Initialized Assertion Predicate for the value of the initial main-loop.
|
||||
|
@ -2834,7 +2834,7 @@ Node* CountedLoopNode::skip_assertion_predicates_with_halt() {
|
||||
ctrl = skip_strip_mined()->in(LoopNode::EntryControl);
|
||||
}
|
||||
if (is_main_loop() || is_post_loop()) {
|
||||
AssertionPredicatesWithHalt assertion_predicates(ctrl);
|
||||
AssertionPredicates assertion_predicates(ctrl);
|
||||
return assertion_predicates.entry();
|
||||
}
|
||||
return ctrl;
|
||||
@ -4470,7 +4470,7 @@ void PhaseIdealLoop::collect_useful_template_assertion_predicates_for_loop(Ideal
|
||||
const PredicateBlock* profiled_loop_predicate_block = predicates.profiled_loop_predicate_block();
|
||||
if (profiled_loop_predicate_block->has_parse_predicate()) {
|
||||
ParsePredicateSuccessProj* parse_predicate_proj = profiled_loop_predicate_block->parse_predicate_success_proj();
|
||||
get_assertion_predicates(parse_predicate_proj, useful_predicates, true);
|
||||
get_template_assertion_predicates(parse_predicate_proj, useful_predicates, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4478,7 +4478,7 @@ void PhaseIdealLoop::collect_useful_template_assertion_predicates_for_loop(Ideal
|
||||
const PredicateBlock* loop_predicate_block = predicates.loop_predicate_block();
|
||||
if (loop_predicate_block->has_parse_predicate()) {
|
||||
ParsePredicateSuccessProj* parse_predicate_proj = loop_predicate_block->parse_predicate_success_proj();
|
||||
get_assertion_predicates(parse_predicate_proj, useful_predicates, true);
|
||||
get_template_assertion_predicates(parse_predicate_proj, useful_predicates, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -947,7 +947,7 @@ private:
|
||||
DEBUG_ONLY(static bool assertion_predicate_has_loop_opaque_node(IfNode* iff);)
|
||||
private:
|
||||
DEBUG_ONLY(static void count_opaque_loop_nodes(Node* n, uint& init, uint& stride);)
|
||||
static void get_assertion_predicates(ParsePredicateSuccessProj* parse_predicate_proj, Unique_Node_List& list, bool get_opaque = false);
|
||||
static void get_template_assertion_predicates(ParsePredicateSuccessProj* parse_predicate_proj, Unique_Node_List& list, bool get_opaque = false);
|
||||
void update_main_loop_assertion_predicates(CountedLoopNode* main_loop_head);
|
||||
void initialize_assertion_predicates_for_peeled_loop(CountedLoopNode* peeled_loop_head,
|
||||
CountedLoopNode* remaining_loop_head,
|
||||
@ -1389,10 +1389,8 @@ public:
|
||||
void loop_predication_follow_branches(Node *c, IdealLoopTree *loop, float loop_trip_cnt,
|
||||
PathFrequency& pf, Node_Stack& stack, VectorSet& seen,
|
||||
Node_List& if_proj_list);
|
||||
IfTrueNode* create_template_assertion_predicate(int if_opcode, CountedLoopNode* loop_head,
|
||||
ParsePredicateSuccessProj* parse_predicate_proj,
|
||||
IfProjNode* new_control, int scale, Node* offset,
|
||||
Node* range, Deoptimization::DeoptReason deopt_reason);
|
||||
IfTrueNode* create_template_assertion_predicate(CountedLoopNode* loop_head, ParsePredicateNode* parse_predicate,
|
||||
IfProjNode* new_control, int scale, Node* offset, Node* range);
|
||||
void eliminate_hoisted_range_check(IfTrueNode* hoisted_check_proj, IfTrueNode* template_assertion_predicate_proj);
|
||||
|
||||
// Helper function to collect predicate for eliminating the useless ones
|
||||
@ -1661,23 +1659,24 @@ private:
|
||||
public:
|
||||
// Clone Parse Predicates to slow and fast loop when unswitching a loop
|
||||
void clone_parse_and_assertion_predicates_to_unswitched_loop(IdealLoopTree* loop, Node_List& old_new,
|
||||
IfProjNode*& iffast_pred, IfProjNode*& ifslow_pred);
|
||||
IfProjNode*& true_path_loop_entry,
|
||||
IfProjNode*& false_path_loop_entry);
|
||||
private:
|
||||
void clone_loop_predication_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new,
|
||||
const PredicateBlock* predicate_block,
|
||||
Deoptimization::DeoptReason reason, IfProjNode*& iffast_pred,
|
||||
IfProjNode*& ifslow_pred);
|
||||
Deoptimization::DeoptReason reason,
|
||||
IfProjNode*& true_path_loop_entry,
|
||||
IfProjNode*& false_path_loop_entry);
|
||||
void clone_parse_predicate_to_unswitched_loops(const PredicateBlock* predicate_block, Deoptimization::DeoptReason reason,
|
||||
IfProjNode*& iffast_pred, IfProjNode*& ifslow_pred);
|
||||
IfProjNode* clone_parse_predicate_to_unswitched_loop(ParsePredicateSuccessProj* parse_predicate_proj, Node* new_entry,
|
||||
Deoptimization::DeoptReason reason, bool slow_loop);
|
||||
void clone_assertion_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new,
|
||||
Deoptimization::DeoptReason reason, ParsePredicateSuccessProj* old_parse_predicate_proj,
|
||||
ParsePredicateSuccessProj* fast_loop_parse_predicate_proj,
|
||||
ParsePredicateSuccessProj* slow_loop_parse_predicate_proj);
|
||||
IfProjNode* clone_assertion_predicate_for_unswitched_loops(IfNode* template_assertion_predicate, IfProjNode* predicate,
|
||||
Deoptimization::DeoptReason reason,
|
||||
ParsePredicateSuccessProj* parse_predicate_proj);
|
||||
ParsePredicateSuccessProj* old_parse_predicate_proj,
|
||||
ParsePredicateNode* true_path_loop_parse_predicate,
|
||||
ParsePredicateNode* false_path_loop_parse_predicate);
|
||||
IfTrueNode* clone_assertion_predicate_for_unswitched_loops(IfTrueNode* template_assertion_predicate_success_proj,
|
||||
ParsePredicateNode* unswitched_loop_parse_predicate);
|
||||
static void check_cloned_parse_predicate_for_unswitching(const Node* new_entry, bool is_fast_loop) PRODUCT_RETURN;
|
||||
|
||||
bool _created_loop_node;
|
||||
|
@ -33,10 +33,10 @@
|
||||
|
||||
// Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate
|
||||
// (i.e. not belonging to an Initialized Assertion Predicate anymore)
|
||||
Node* AssertionPredicatesWithHalt::find_entry(Node* start_proj) {
|
||||
Node* AssertionPredicates::find_entry(Node* start_proj) {
|
||||
assert(start_proj != nullptr, "should not be null");
|
||||
Node* entry = start_proj;
|
||||
while (AssertionPredicateWithHalt::is_predicate(entry)) {
|
||||
while (AssertionPredicate::is_predicate(entry)) {
|
||||
entry = entry->in(0)->in(0);
|
||||
}
|
||||
return entry;
|
||||
@ -48,7 +48,7 @@ bool may_be_assertion_predicate_if(const Node* node) {
|
||||
return node->is_IfTrue() && RegularPredicate::may_be_predicate_if(node->as_IfProj());
|
||||
}
|
||||
|
||||
bool AssertionPredicateWithHalt::is_predicate(const Node* maybe_success_proj) {
|
||||
bool AssertionPredicate::is_predicate(const Node* maybe_success_proj) {
|
||||
if (!may_be_assertion_predicate_if(maybe_success_proj)) {
|
||||
return false;
|
||||
}
|
||||
@ -57,14 +57,14 @@ bool AssertionPredicateWithHalt::is_predicate(const Node* maybe_success_proj) {
|
||||
|
||||
// Check if the If node of `predicate_proj` has an OpaqueTemplateAssertionPredicate (Template Assertion Predicate) or
|
||||
// an OpaqueInitializedAssertionPredicate (Initialized Assertion Predicate) node as input.
|
||||
bool AssertionPredicateWithHalt::has_assertion_predicate_opaque(const Node* predicate_proj) {
|
||||
bool AssertionPredicate::has_assertion_predicate_opaque(const Node* predicate_proj) {
|
||||
IfNode* iff = predicate_proj->in(0)->as_If();
|
||||
Node* bol = iff->in(1);
|
||||
return bol->is_OpaqueTemplateAssertionPredicate() || bol->is_OpaqueInitializedAssertionPredicate();
|
||||
}
|
||||
|
||||
// Check if the other projection (UCT projection) of `success_proj` has a Halt node as output.
|
||||
bool AssertionPredicateWithHalt::has_halt(const Node* success_proj) {
|
||||
bool AssertionPredicate::has_halt(const Node* success_proj) {
|
||||
ProjNode* other_proj = success_proj->as_IfProj()->other_if_proj();
|
||||
return other_proj->outcnt() == 1 && other_proj->unique_out()->Opcode() == Op_Halt;
|
||||
}
|
||||
@ -82,7 +82,7 @@ ParsePredicateNode* ParsePredicate::init_parse_predicate(Node* parse_predicate_p
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Deoptimization::DeoptReason RegularPredicateWithUCT::uncommon_trap_reason(IfProjNode* if_proj) {
|
||||
Deoptimization::DeoptReason RuntimePredicate::uncommon_trap_reason(IfProjNode* if_proj) {
|
||||
CallStaticJavaNode* uct_call = if_proj->is_uncommon_trap_if_pattern();
|
||||
if (uct_call == nullptr) {
|
||||
return Deoptimization::Reason_none;
|
||||
@ -90,7 +90,7 @@ Deoptimization::DeoptReason RegularPredicateWithUCT::uncommon_trap_reason(IfProj
|
||||
return Deoptimization::trap_request_reason(uct_call->uncommon_trap_request());
|
||||
}
|
||||
|
||||
bool RegularPredicateWithUCT::is_predicate(Node* maybe_success_proj) {
|
||||
bool RuntimePredicate::is_predicate(Node* maybe_success_proj) {
|
||||
if (RegularPredicate::may_be_predicate_if(maybe_success_proj)) {
|
||||
return has_valid_uncommon_trap(maybe_success_proj);
|
||||
} else {
|
||||
@ -98,7 +98,7 @@ bool RegularPredicateWithUCT::is_predicate(Node* maybe_success_proj) {
|
||||
}
|
||||
}
|
||||
|
||||
bool RegularPredicateWithUCT::has_valid_uncommon_trap(const Node* success_proj) {
|
||||
bool RuntimePredicate::has_valid_uncommon_trap(const Node* success_proj) {
|
||||
assert(RegularPredicate::may_be_predicate_if(success_proj), "must have been checked before");
|
||||
const Deoptimization::DeoptReason deopt_reason = uncommon_trap_reason(success_proj->as_IfProj());
|
||||
return (deopt_reason == Deoptimization::Reason_loop_limit_check ||
|
||||
@ -106,7 +106,7 @@ bool RegularPredicateWithUCT::has_valid_uncommon_trap(const Node* success_proj)
|
||||
deopt_reason == Deoptimization::Reason_profile_predicate);
|
||||
}
|
||||
|
||||
bool RegularPredicateWithUCT::is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason) {
|
||||
bool RuntimePredicate::is_predicate(const Node* node, const Deoptimization::DeoptReason deopt_reason) {
|
||||
if (RegularPredicate::may_be_predicate_if(node)) {
|
||||
return deopt_reason == uncommon_trap_reason(node->as_IfProj());
|
||||
} else {
|
||||
@ -128,16 +128,6 @@ bool RegularPredicate::may_be_predicate_if(const Node* node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Runtime Predicates always have an UCT since they could normally fail at runtime. In this case we execute the trap
|
||||
// on the failing path.
|
||||
bool RuntimePredicate::is_predicate(Node* node) {
|
||||
return RegularPredicateWithUCT::is_predicate(node);
|
||||
}
|
||||
|
||||
bool RuntimePredicate::is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason) {
|
||||
return RegularPredicateWithUCT::is_predicate(node, deopt_reason);
|
||||
}
|
||||
|
||||
// Rewire any non-CFG nodes dependent on this Template Assertion Predicate (i.e. with a control input to this
|
||||
// Template Assertion Predicate) to the 'target_predicate' based on the 'data_in_loop_body' check.
|
||||
void TemplateAssertionPredicate::rewire_loop_data_dependencies(IfTrueNode* target_predicate,
|
||||
@ -161,6 +151,21 @@ bool TemplateAssertionPredicate::is_predicate(Node* node) {
|
||||
return if_node->in(1)->is_OpaqueTemplateAssertionPredicate();
|
||||
}
|
||||
|
||||
// Clone this Template Assertion Predicate and replace the OpaqueLoopInitNode with the provided 'new_opaque_init' node.
|
||||
IfTrueNode* TemplateAssertionPredicate::clone(Node* new_control, PhaseIdealLoop* phase) const {
|
||||
assert(PhaseIdealLoop::assertion_predicate_has_loop_opaque_node(_if_node),
|
||||
"must find OpaqueLoop* nodes for Template Assertion Predicate");
|
||||
TemplateAssertionExpression template_assertion_expression(opaque_node());
|
||||
OpaqueTemplateAssertionPredicateNode* new_opaque_node = template_assertion_expression.clone(new_control, phase);
|
||||
AssertionPredicateIfCreator assertion_predicate_if_creator(phase);
|
||||
IfTrueNode* success_proj = assertion_predicate_if_creator.create_for_template(new_control, _if_node->Opcode(),
|
||||
new_opaque_node,
|
||||
_if_node->assertion_predicate_type());
|
||||
assert(PhaseIdealLoop::assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()),
|
||||
"Template Assertion Predicates must have OpaqueLoop* nodes in the bool expression");
|
||||
return success_proj;
|
||||
}
|
||||
|
||||
// Clone this Template Assertion Predicate and replace the OpaqueLoopInitNode with the provided 'new_opaque_init' node.
|
||||
IfTrueNode* TemplateAssertionPredicate::clone_and_replace_init(Node* new_control, OpaqueLoopInitNode* new_opaque_init,
|
||||
PhaseIdealLoop* phase) const {
|
||||
@ -168,7 +173,7 @@ IfTrueNode* TemplateAssertionPredicate::clone_and_replace_init(Node* new_control
|
||||
"must find OpaqueLoop* nodes for Template Assertion Predicate");
|
||||
TemplateAssertionExpression template_assertion_expression(opaque_node());
|
||||
OpaqueTemplateAssertionPredicateNode* new_opaque_node =
|
||||
template_assertion_expression.clone_and_replace_init(new_opaque_init, new_control, phase);
|
||||
template_assertion_expression.clone_and_replace_init(new_control, new_opaque_init, phase);
|
||||
AssertionPredicateIfCreator assertion_predicate_if_creator(phase);
|
||||
IfTrueNode* success_proj = assertion_predicate_if_creator.create_for_template(new_control, _if_node->Opcode(),
|
||||
new_opaque_node,
|
||||
@ -295,16 +300,16 @@ class ReplaceInitAndStrideStrategy : public TransformStrategyForOpaqueLoopNodes
|
||||
// OpaqueTemplateAssertionPredicate to and including the OpaqueLoop* nodes). The cloned nodes are rewired to reflect the
|
||||
// same graph structure as found for this Template Assertion Expression. The cloned nodes get 'new_ctrl' as ctrl. There
|
||||
// is no other update done for the cloned nodes. Return the newly cloned OpaqueTemplateAssertionPredicate.
|
||||
OpaqueTemplateAssertionPredicateNode* TemplateAssertionExpression::clone(Node* new_ctrl, PhaseIdealLoop* phase) {
|
||||
CloneStrategy clone_init_and_stride_strategy(phase, new_ctrl);
|
||||
return clone(clone_init_and_stride_strategy, new_ctrl, phase);
|
||||
OpaqueTemplateAssertionPredicateNode* TemplateAssertionExpression::clone(Node* new_control, PhaseIdealLoop* phase) {
|
||||
CloneStrategy clone_init_and_stride_strategy(phase, new_control);
|
||||
return clone(clone_init_and_stride_strategy, new_control, phase);
|
||||
}
|
||||
|
||||
// Same as clone() but instead of cloning the OpaqueLoopInitNode, we replace it with the provided 'new_init' node.
|
||||
OpaqueTemplateAssertionPredicateNode*
|
||||
TemplateAssertionExpression::clone_and_replace_init(Node* new_init, Node* new_ctrl, PhaseIdealLoop* phase) {
|
||||
ReplaceInitAndCloneStrideStrategy replace_init_and_clone_stride_strategy(new_init, new_ctrl, phase);
|
||||
return clone(replace_init_and_clone_stride_strategy, new_ctrl, phase);
|
||||
TemplateAssertionExpression::clone_and_replace_init(Node* new_control, Node* new_init, PhaseIdealLoop* phase) {
|
||||
ReplaceInitAndCloneStrideStrategy replace_init_and_clone_stride_strategy(new_init, new_control, phase);
|
||||
return clone(replace_init_and_clone_stride_strategy, new_control, phase);
|
||||
}
|
||||
|
||||
// Same as clone() but instead of cloning the OpaqueLoopInit and OpaqueLoopStride node, we replace them with the provided
|
||||
@ -617,26 +622,6 @@ void AssertionPredicateIfCreator::create_halt_node(IfFalseNode* fail_proj, Ideal
|
||||
_phase->register_control(halt, loop, fail_proj);
|
||||
}
|
||||
|
||||
// Creates an init and last value Template Assertion Predicate connected together from a Parse Predicate with an UCT on
|
||||
// the failing path. Returns the success projection of the last value Template Assertion Predicate.
|
||||
IfTrueNode* TemplateAssertionPredicateCreator::create_with_uncommon_trap(
|
||||
Node* new_control, ParsePredicateSuccessProj* parse_predicate_success_proj,
|
||||
const Deoptimization::DeoptReason deopt_reason, const int if_opcode) {
|
||||
OpaqueLoopInitNode* opaque_init = create_opaque_init(new_control);
|
||||
bool does_overflow;
|
||||
OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression =
|
||||
create_for_init_value(new_control, opaque_init, does_overflow);
|
||||
IfTrueNode* template_predicate_success_proj =
|
||||
create_if_node_with_uncommon_trap(template_assertion_predicate_expression, parse_predicate_success_proj,
|
||||
deopt_reason, if_opcode, does_overflow,
|
||||
AssertionPredicateType::InitValue);
|
||||
template_assertion_predicate_expression = create_for_last_value(template_predicate_success_proj, opaque_init,
|
||||
does_overflow);
|
||||
return create_if_node_with_uncommon_trap(template_assertion_predicate_expression, parse_predicate_success_proj,
|
||||
deopt_reason, if_opcode, does_overflow,
|
||||
AssertionPredicateType::LastValue);
|
||||
}
|
||||
|
||||
OpaqueLoopInitNode* TemplateAssertionPredicateCreator::create_opaque_init(Node* new_control) {
|
||||
OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->init_trip());
|
||||
_phase->register_new_node(opaque_init, new_control);
|
||||
@ -650,17 +635,6 @@ TemplateAssertionPredicateCreator::create_for_init_value(Node* new_control, Opaq
|
||||
return expression_creator.create_for_template(new_control, opaque_init, does_overflow);
|
||||
}
|
||||
|
||||
IfTrueNode* TemplateAssertionPredicateCreator::create_if_node_with_uncommon_trap(
|
||||
OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression,
|
||||
ParsePredicateSuccessProj* parse_predicate_success_proj, const Deoptimization::DeoptReason deopt_reason,
|
||||
const int if_opcode, const bool does_overflow, const AssertionPredicateType assertion_predicate_type) {
|
||||
IfTrueNode* success_proj = _phase->create_new_if_for_predicate(parse_predicate_success_proj, nullptr, deopt_reason,
|
||||
does_overflow ? Op_If : if_opcode, false,
|
||||
assertion_predicate_type);
|
||||
_phase->igvn().replace_input_of(success_proj->in(0), 1, template_assertion_predicate_expression);
|
||||
return success_proj;
|
||||
}
|
||||
|
||||
OpaqueTemplateAssertionPredicateNode*
|
||||
TemplateAssertionPredicateCreator::create_for_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init,
|
||||
bool& does_overflow) const {
|
||||
@ -683,7 +657,7 @@ Node* TemplateAssertionPredicateCreator::create_last_value(Node* new_control, Op
|
||||
return last_value;
|
||||
}
|
||||
|
||||
IfTrueNode* TemplateAssertionPredicateCreator::create_if_node_with_halt(
|
||||
IfTrueNode* TemplateAssertionPredicateCreator::create_if_node(
|
||||
Node* new_control, OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression,
|
||||
const bool does_overflow, const AssertionPredicateType assertion_predicate_type) {
|
||||
AssertionPredicateIfCreator assertion_predicate_if_creator(_phase);
|
||||
@ -694,18 +668,18 @@ IfTrueNode* TemplateAssertionPredicateCreator::create_if_node_with_halt(
|
||||
|
||||
// Creates an init and last value Template Assertion Predicate connected together with a Halt node on the failing path.
|
||||
// Returns the success projection of the last value Template Assertion Predicate latter.
|
||||
IfTrueNode* TemplateAssertionPredicateCreator::create_with_halt(Node* new_control) {
|
||||
IfTrueNode* TemplateAssertionPredicateCreator::create(Node* new_control) {
|
||||
OpaqueLoopInitNode* opaque_init = create_opaque_init(new_control);
|
||||
bool does_overflow;
|
||||
OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression =
|
||||
create_for_init_value(new_control, opaque_init, does_overflow);
|
||||
IfTrueNode* template_predicate_success_proj =
|
||||
create_if_node_with_halt(new_control, template_assertion_predicate_expression, does_overflow,
|
||||
AssertionPredicateType::InitValue);
|
||||
create_if_node(new_control, template_assertion_predicate_expression, does_overflow,
|
||||
AssertionPredicateType::InitValue);
|
||||
template_assertion_predicate_expression = create_for_last_value(template_predicate_success_proj, opaque_init,
|
||||
does_overflow);
|
||||
return create_if_node_with_halt(template_predicate_success_proj, template_assertion_predicate_expression,
|
||||
does_overflow, AssertionPredicateType::LastValue);
|
||||
return create_if_node(template_predicate_success_proj, template_assertion_predicate_expression,
|
||||
does_overflow, AssertionPredicateType::LastValue);
|
||||
}
|
||||
|
||||
InitializedAssertionPredicateCreator::InitializedAssertionPredicateCreator(PhaseIdealLoop* phase)
|
||||
|
@ -252,15 +252,15 @@ class NodeInLoopBody : public StackObj {
|
||||
virtual bool check(Node* node) const = 0;
|
||||
};
|
||||
|
||||
// Class to represent Assertion Predicates with a HaltNode instead of an UCT (i.e. either an Initialized Assertion
|
||||
// Predicate or a Template Assertion Predicate created after the initial one at Loop Predication).
|
||||
class AssertionPredicatesWithHalt : public StackObj {
|
||||
Node* _entry;
|
||||
// Class to represent Assertion Predicates (i.e. either Initialized and/or Template Assertion Predicates).
|
||||
class AssertionPredicates : public StackObj {
|
||||
Node* const _entry;
|
||||
|
||||
static Node* find_entry(Node* start_proj);
|
||||
|
||||
public:
|
||||
AssertionPredicatesWithHalt(Node* assertion_predicate_proj) : _entry(find_entry(assertion_predicate_proj)) {}
|
||||
explicit AssertionPredicates(Node* assertion_predicate_proj) : _entry(find_entry(assertion_predicate_proj)) {}
|
||||
NONCOPYABLE(AssertionPredicates);
|
||||
|
||||
// Returns the control input node into the first assertion predicate If. If there are no assertion predicates, it
|
||||
// returns the same node initially passed to the constructor.
|
||||
@ -269,15 +269,14 @@ class AssertionPredicatesWithHalt : public StackObj {
|
||||
}
|
||||
};
|
||||
|
||||
// Class to represent a single Assertion Predicate with a HaltNode. This could either be:
|
||||
// Class to represent a single Assertion Predicate. This could either be:
|
||||
// - A Template Assertion Predicate.
|
||||
// - An Initialized Assertion Predicate.
|
||||
// Note that all other Regular Predicates have an UCT node.
|
||||
class AssertionPredicateWithHalt : public StackObj {
|
||||
class AssertionPredicate : public StackObj {
|
||||
static bool has_assertion_predicate_opaque(const Node* predicate_proj);
|
||||
static bool has_halt(const Node* success_proj);
|
||||
public:
|
||||
static bool is_predicate(const Node* maybe_success_proj);
|
||||
static bool has_halt(const Node* success_proj);
|
||||
};
|
||||
|
||||
// Utility class representing a Regular Predicate which is either a Runtime Predicate or an Assertion Predicate.
|
||||
@ -286,19 +285,6 @@ class RegularPredicate : public StackObj {
|
||||
static bool may_be_predicate_if(const Node* node);
|
||||
};
|
||||
|
||||
// Class to represent a single Regular Predicate with an UCT. This could either be:
|
||||
// - A Runtime Predicate
|
||||
// - A Template Assertion Predicate
|
||||
// Note that all other Regular Predicates have a Halt node.
|
||||
class RegularPredicateWithUCT : public StackObj {
|
||||
static Deoptimization::DeoptReason uncommon_trap_reason(IfProjNode* if_proj);
|
||||
|
||||
public:
|
||||
static bool is_predicate(Node* maybe_success_proj);
|
||||
static bool is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason);
|
||||
static bool has_valid_uncommon_trap(const Node* success_proj);
|
||||
};
|
||||
|
||||
// Class to represent a Parse Predicate.
|
||||
class ParsePredicate : public Predicate {
|
||||
ParsePredicateSuccessProj* _success_proj;
|
||||
@ -356,6 +342,8 @@ class RuntimePredicate : public Predicate {
|
||||
|
||||
private:
|
||||
static bool is_predicate(Node* maybe_success_proj);
|
||||
static bool has_valid_uncommon_trap(const Node* success_proj);
|
||||
static Deoptimization::DeoptReason uncommon_trap_reason(IfProjNode* if_proj);
|
||||
|
||||
public:
|
||||
Node* entry() const override {
|
||||
@ -370,7 +358,7 @@ class RuntimePredicate : public Predicate {
|
||||
return _success_proj;
|
||||
}
|
||||
|
||||
static bool is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason);
|
||||
static bool is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason);
|
||||
};
|
||||
|
||||
// Class to represent a Template Assertion Predicate.
|
||||
@ -405,6 +393,7 @@ class TemplateAssertionPredicate : public Predicate {
|
||||
return _if_node->assertion_predicate_type() == AssertionPredicateType::LastValue;
|
||||
}
|
||||
|
||||
IfTrueNode* clone(Node* new_control, PhaseIdealLoop* phase) const;
|
||||
IfTrueNode* clone_and_replace_init(Node* new_control, OpaqueLoopInitNode* new_opaque_init, PhaseIdealLoop* phase) const;
|
||||
void replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) const;
|
||||
IfTrueNode* initialize(PhaseIdealLoop* phase, Node* new_control) const;
|
||||
@ -466,8 +455,9 @@ class TemplateAssertionExpression : public StackObj {
|
||||
Node* new_ctrl, PhaseIdealLoop* phase);
|
||||
|
||||
public:
|
||||
OpaqueTemplateAssertionPredicateNode* clone(Node* new_ctrl, PhaseIdealLoop* phase);
|
||||
OpaqueTemplateAssertionPredicateNode* clone_and_replace_init(Node* new_init, Node* new_ctrl, PhaseIdealLoop* phase);
|
||||
OpaqueTemplateAssertionPredicateNode* clone(Node* new_control, PhaseIdealLoop* phase);
|
||||
OpaqueTemplateAssertionPredicateNode* clone_and_replace_init(Node* new_control, Node* new_init,
|
||||
PhaseIdealLoop* phase);
|
||||
OpaqueTemplateAssertionPredicateNode* clone_and_replace_init_and_stride(Node* new_control, Node* new_init,
|
||||
Node* new_stride, PhaseIdealLoop* phase);
|
||||
void replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn);
|
||||
@ -570,7 +560,7 @@ class AssertionPredicateIfCreator : public StackObj {
|
||||
void create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop, const char* halt_message);
|
||||
};
|
||||
|
||||
// This class is used to create a Template Assertion Predicate either with an UCT or a Halt Node from scratch.
|
||||
// This class is used to create a Template Assertion Predicate either with a Halt Node from scratch.
|
||||
class TemplateAssertionPredicateCreator : public StackObj {
|
||||
CountedLoopNode* const _loop_head;
|
||||
const int _scale;
|
||||
@ -584,13 +574,9 @@ class TemplateAssertionPredicateCreator : public StackObj {
|
||||
OpaqueTemplateAssertionPredicateNode* create_for_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init,
|
||||
bool& does_overflow) const;
|
||||
Node* create_last_value(Node* new_control, OpaqueLoopInitNode* opaque_init) const;
|
||||
IfTrueNode* create_if_node_with_uncommon_trap(OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression,
|
||||
ParsePredicateSuccessProj* parse_predicate_success_proj,
|
||||
Deoptimization::DeoptReason deopt_reason, int if_opcode,
|
||||
bool does_overflow, AssertionPredicateType assertion_predicate_type);
|
||||
IfTrueNode* create_if_node_with_halt(Node* new_control,
|
||||
OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression,
|
||||
bool does_overflow, AssertionPredicateType assertion_predicate_type);
|
||||
IfTrueNode* create_if_node(Node* new_control,
|
||||
OpaqueTemplateAssertionPredicateNode* template_assertion_predicate_expression,
|
||||
bool does_overflow, AssertionPredicateType assertion_predicate_type);
|
||||
|
||||
public:
|
||||
TemplateAssertionPredicateCreator(CountedLoopNode* loop_head, int scale, Node* offset, Node* range,
|
||||
@ -602,9 +588,7 @@ class TemplateAssertionPredicateCreator : public StackObj {
|
||||
_phase(phase) {}
|
||||
NONCOPYABLE(TemplateAssertionPredicateCreator);
|
||||
|
||||
IfTrueNode* create_with_uncommon_trap(Node* new_control, ParsePredicateSuccessProj* parse_predicate_success_proj,
|
||||
Deoptimization::DeoptReason deopt_reason, int if_opcode);
|
||||
IfTrueNode* create_with_halt(Node* new_control);
|
||||
IfTrueNode* create(Node* new_control);
|
||||
};
|
||||
|
||||
// This class creates a new Initialized Assertion Predicate either from a template or from scratch.
|
||||
|
Loading…
x
Reference in New Issue
Block a user