RFR: 8347901: C2 should remove unused leaf / pure runtime calls
Emanuel Peter
epeter at openjdk.org
Wed Jun 18 14:51:38 UTC 2025
On Wed, 11 Jun 2025 16:18:41 GMT, Marc Chevalier <mchevalier at openjdk.org> wrote:
> A first part toward a better support of pure functions, but this time, with guidance from @iwanowww.
>
> ## Pure Functions
>
> Pure functions (considered here) are functions that have no side effects, no effect on the control flow (no exception or such), cannot deopt etc.. It's really a function that you can execute anywhere, with whichever arguments without effect other than wasting time. Integer division is not pure as dividing by zero is throwing. But many floating point functions will just return `NaN` or `+/-infinity` in problematic cases.
>
> ## Scope
>
> We are not going all powerful for now! It's mostly about identifying some pure functions and being able to remove them if the result is unused. Some other things are not part of this PR, on purpose. Especially, this PR doesn't propose a way to move pure calls around. The reason is that pure calls are later expanded into regular calls, which require a control input. To be able to do the expansion, we just keep the control in the pure call as well.
>
> ## Implementation Overview
>
> We created here some new node kind for pure calls, inheriting leaf calls, that are expanded into regular leaf calls during final graph reshaping. The possibility to support pure call directly in AD file is left open.
>
> This PR also introduces `TupleNode` (largely based on an original idea/implem of @iwanowww), that just tie multiple input together and play well with `ProjNode`: the n-th projection of a `TupleNode` is the n-th input of the tuple. This is a convenient way to skip and remove nodes from the graph while delegating the difficulty of the surgery to the trusted IGVN's implementation.
>
> Thanks,
> Marc
@marc-chevalier Nice work!
I have a first set of qestions / suggestions :)
src/hotspot/share/opto/callnode.cpp line 1306:
> 1304:
> 1305: //=============================================================================
> 1306: bool CallLeafPureNode::is_unused() const {
Can you add a quick comment why this check implies that the node is not used, i.e. what that means?
src/hotspot/share/opto/callnode.cpp line 1309:
> 1307: return proj_out_or_null(TypeFunc::Parms) == nullptr;
> 1308: }
> 1309: // We make a tuple of the global input state + TOP for the output values.
Suggestion:
}
// We make a tuple of the global input state + TOP for the output values.
Nit: there should be a newline between the methods.
src/hotspot/share/opto/callnode.cpp line 1335:
> 1333: if (can_reshape && is_unused()) {
> 1334: return make_tuple_of_input_state_and_top_return_values(phase->C);
> 1335: }
Can you add a code comment what this does?
src/hotspot/share/opto/compile.cpp line 3302:
> 3300: break;
> 3301: case Op_CallLeafPure: {
> 3302: if (!Matcher::match_rule_supported(Op_CallLeafPure)) {
Suggestion:
// If the pure call is not supported, then lower to a CallLeaf.
if (!Matcher::match_rule_supported(Op_CallLeafPure)) {
src/hotspot/share/opto/compile.cpp line 3313:
> 3311: for (unsigned int i = 0; i < call->tf()->domain()->cnt() - TypeFunc::Parms; i++) {
> 3312: new_call->init_req(TypeFunc::Parms + i, call->in(TypeFunc::Parms + i));
> 3313: }
The `TypeFunc::Parms` offsets are a bit confusing / unnecessary. Why not:
Suggestion:
// Copy all <something>.
for (unsigned int i = TypeFunc::Parms; i < call->tf()->domain()->cnt(); i++) {
new_call->init_req(i, call->in(i));
}
src/hotspot/share/opto/divnode.cpp line 1523:
> 1521: return nullptr;
> 1522: }
> 1523: if (proj_out_or_null(TypeFunc::Control) == nullptr) { // dead node
Interesting. You have extracted a method for `is_unused`, which you use multiple times. But you did not do it for this check which you also use multiple times. You could also wrap this in some method like `is_dead`.
src/hotspot/share/opto/divnode.cpp line 1530:
> 1528:
> 1529: if (is_unused()) {
> 1530: return make_tuple_of_input_state_and_top_return_values(igvn->C);
What does this do? Add a short comment :)
src/hotspot/share/opto/divnode.cpp line 1642:
> 1640: }
> 1641: assert(projs.catchall_ioproj == nullptr, "no exceptions from floating mod");
> 1642: assert(projs.catchall_catchproj == nullptr, "no exceptions from floating mod");
Why were you able to remove this?
src/hotspot/share/opto/graphKit.cpp line 1916:
> 1914: if (call->is_CallLeafPure()) {
> 1915: return;
> 1916: }
Suggestion:
if (call->is_CallLeafPure()) {
// <your comment why you exit here>
return;
}
src/hotspot/share/opto/multnode.cpp line 126:
> 124: // Jumping over Tuples: the i-th projection of a Tuple is the i-th input of the Tuple.
> 125: ctrl = ctrl->in(_con);
> 126: }
Do you need to special-case this here? Why does the `ProjNode::Identity` not suffice? Are there potentially other locations where we now would need this special logic?
src/hotspot/share/opto/multnode.cpp line 177:
> 175: }
> 176: return this;
> 177: }
What would happen if we miss to do this optimization? I suppose we would have a Tuple left in the graph and get a bad AD file assert / deopt in product?
src/hotspot/share/opto/multnode.hpp line 109:
> 107: };
> 108:
> 109: //------------------------------TupleNode---------------------------------------
These lines are rather unnecessary. More useful would be a description of what this Tuple is, and what it is used for. Are there any invariants about it? When do we expect them to appear or disappear?
src/hotspot/share/opto/multnode.hpp line 113:
> 111: const TypeTuple* _tf;
> 112:
> 113: template <typename... NN>
Does this need to be a template? Or would a type like `Node*` or `Node` suffice?
-------------
Changes requested by epeter (Reviewer).
PR Review: https://git.openjdk.org/jdk/pull/25760#pullrequestreview-2938357614
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154786291
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154059786
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154386828
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154770330
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154767905
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154784765
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154788329
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154790934
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154795672
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154804710
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154806581
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154808639
PR Review Comment: https://git.openjdk.org/jdk/pull/25760#discussion_r2154812833
More information about the graal-dev
mailing list