RFR: 8345766: C2 should emit macro nodes for ModF/ModD instead of calls during parsing

Vladimir Kozlov kvn at openjdk.org
Tue Jan 7 18:29:44 UTC 2025


On Tue, 17 Dec 2024 09:01:57 GMT, Theo Weidmann <tweidmann at openjdk.org> wrote:

> C2 currently emits runtime calls if the platform rules do not support lowering floating point remainder operations. For example, for float:
> 
> https://github.com/openjdk/jdk/blob/fbbc7c35f422294090b8c7a02a19ab2fb67c7070/src/hotspot/share/opto/parse2.cpp#L2305-L2318
> 
> https://github.com/openjdk/jdk/blob/fbbc7c35f422294090b8c7a02a19ab2fb67c7070/src/hotspot/share/opto/parse2.cpp#L1099-L1109
> 
> The only platform, which currently supports this, however, is x86_32. On all other platforms, runtime calls are generated directly during parsing, which prevent any constant folding or other idealizations. Even C1 can perform these optimizations, resulting in significantly lower C2 performance compared to C1 for simple test cases. This function was observed to be around 15x slower with C2 compared to C1 due to redundant runtime calls:
> 
> 
> public static double process(final double x) {
>         double w = (double) 0.1;
>         double p = 0;
>         p = (double) (3.109615012413746E307 % (w % Z));
>         p = (double) (7.614949555185036E307 / (x % x)); // <- return value only dependends on this line
>         return (double) (x * p);
> }
> 
> 
> To fix this, this PR turns ModFNode and ModDNode into macros, which are always created during parsing. They support idealization (constant folding) and are lowered to runtime calls during macro expansion. For simplicity, these operations will now also call into the runtime on x86_32, as this platform is deprecated.

Most likely it will affect performance of 32-bit x86 since it was simple nodes and not Call nodes. But the platforma is going away so it should be fine.

You need to treat new Mod nodes as leaf calls without side effects instead of general Call nodes.

src/hotspot/share/opto/callnode.cpp line 721:

> 719: const Type *CallNode::bottom_type() const { return tf()->range(); }
> 720: const Type* CallNode::Value(PhaseGVN* phase) const {
> 721:   if (!in(0) || phase->type(in(0)) == Type::TOP) {

We use explicit compare `in(0) == nullptr`.

src/hotspot/share/opto/divnode.hpp line 145:

> 143: 
> 144: // Base class for float and double modulus
> 145: class ModFloatingNode : public CallNode {

I think it should subclass `CallLeafNode` class since Mod runtime functions do not have side effects.

src/hotspot/share/opto/macro.cpp line 2602:

> 2600:           call->init_req(TypeFunc::Parms + i, mod_macro->in(TypeFunc::Parms + i));
> 2601:         }
> 2602:         call->copy_call_debug_info(&_igvn, call);

This is used only for `CallJavaNode`.

src/hotspot/share/opto/parse2.cpp line 1100:

> 1098: 
> 1099: Node* Parse::floating_point_mod(Node* a, Node* b, bool dbl) {
> 1100:   CallNode* mod = dbl ? static_cast<CallNode*>(new ModDNode(C, a, b)) : new ModFNode(C, a, b);

Why you need the case for `ModDNode`?

src/hotspot/share/opto/parse2.cpp line 1103:

> 1101: 
> 1102:   Node* prev_mem = set_predefined_input_for_runtime_call(mod);
> 1103:   mod = _gvn.transform(mod)->as_Call();

Is `as_Call()` used to check with assert?

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

PR Review: https://git.openjdk.org/jdk/pull/22786#pullrequestreview-2534921424
PR Review Comment: https://git.openjdk.org/jdk/pull/22786#discussion_r1905780293
PR Review Comment: https://git.openjdk.org/jdk/pull/22786#discussion_r1905867970
PR Review Comment: https://git.openjdk.org/jdk/pull/22786#discussion_r1905877628
PR Review Comment: https://git.openjdk.org/jdk/pull/22786#discussion_r1905785504
PR Review Comment: https://git.openjdk.org/jdk/pull/22786#discussion_r1905788193


More information about the hotspot-compiler-dev mailing list