RFR: 8305638: Refactor Template Assertion Predicate Bool creation and Predicate code in Split If and Loop Unswitching

Emanuel Peter epeter at openjdk.org
Fri Dec 22 17:28:56 UTC 2023


On Fri, 22 Dec 2023 16:47:52 GMT, Emanuel Peter <epeter at openjdk.org> wrote:

>> src/hotspot/share/opto/predicates.cpp line 245:
>> 
>>> 243: // Class to clone a Template Assertion Predicate Bool. The BoolNode and all the nodes up to but excluding the OpaqueLoop*
>>> 244: // nodes are cloned. The OpaqueLoop* nodes are transformed by the provided strategy (e.g. cloned or replaced).
>>> 245: class CloneTemplateAssertionPredicateBool : public StackObj {
>> 
>> Suggestion:
>> 
>> class CloneTemplateAssertionPredicateExpression : public StackObj {
>
> I left quite a few comments below, because I think the algorithm is difficult to understand.
> I like the idea of splitting it into smaller parts.
> 
> If I understand the basic algorithm, we do this:
> DFS, where we traverse the inputs recursively, using the `DFSNodeStack`, which uses the filter `TemplateAssertionPredicateBool::could_be_part`.
> 
> If you see a init/stride `Opaque1` node, then you obviously clone it.
> 
> If you come back from an input to a output (use to def), then you may get back a cloned input node.
> If the input node is not a clone, then we have to do nothing.
> If the input node is a clone, we also have to clone the output node. Clone it if it is not already a clone. And then update the current input slot, so we do not point to the pre-cloned input, but to the cloned input.

So we need to do work whenever we walk back down a input->output edge. We can traverse this edge, by taking the post-order traversal, and then waling from the post-order visited node to its output node.


    Node* current;
    while (_stack.is_not_empty()) {
      current = _stack.top();
      if (current->is_Opaque1()) {
        Node* transformed_node = transform_opaque_loop_node(current, transform_opaque_loop_nodes);
        _stack.replace_top(transformed_node);
        traverse_edge_back_to_output_and_pop();
      } else if (!_stack.push_next_unvisited_input()) {
        traverse_edge_back_to_output_and_pop();
      } // else: we just pushed an new input, go and visit it first
    }
    
traverse_edge_back_to_output_and_pop:
  Node* maybe_cloned_input = _stack.pop();
  if (_stack.is_empty()) {
    return; // we just visited the root node, and are now done. Maybe verify we have the root here?  
  }
  Node* output = _stack.top();
  int i = _stack.top_index();
  if (!is_clone(output)) {
    output = output->clone();
    _stack.replace_top(output);
  }
  output->set_req(i, maybe_cloned_input);

>> src/hotspot/share/opto/predicates.cpp line 273:
>> 
>>> 271:     if (must_clone_node_on_top(transformed_opaque_loop_node)) {
>>> 272:       clone_and_replace_top_node();
>>> 273:     }
>> 
>> Why do you touch the output node here? Is it not post-visited later anyway?
>
> Oh, I see. We are eagerly cloning the output node, if it is not yet cloned. Hmm. I guess that is better than trying to fiture out in the post-visit if we need to clone or not, because that would require querying if any input node is a clone?

Ah oh dear. And that is why you need to then always rewire below. Hmm.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/16877#discussion_r1435231073
PR Review Comment: https://git.openjdk.org/jdk/pull/16877#discussion_r1435193899


More information about the hotspot-compiler-dev mailing list