mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 09:53:18 +00:00
8378742: C2: constant folding for ModFloatingNode should be done in Value method
Reviewed-by: roland, vlivanov, mchevalier
This commit is contained in:
parent
28830e2155
commit
a211b0442d
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -1592,41 +1592,25 @@ const Type* ModDNode::get_result_if_constant(const Type* dividend, const Type* d
|
||||
return TypeD::make(jdouble_cast(xr));
|
||||
}
|
||||
|
||||
Node* ModFloatingNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
if (can_reshape) {
|
||||
PhaseIterGVN* igvn = phase->is_IterGVN();
|
||||
|
||||
// Either input is TOP ==> the result is TOP
|
||||
const Type* dividend_type = phase->type(dividend());
|
||||
const Type* divisor_type = phase->type(divisor());
|
||||
if (dividend_type == Type::TOP || divisor_type == Type::TOP) {
|
||||
return phase->C->top();
|
||||
}
|
||||
const Type* constant_result = get_result_if_constant(dividend_type, divisor_type);
|
||||
if (constant_result != nullptr) {
|
||||
return make_tuple_of_input_state_and_constant_result(igvn, constant_result);
|
||||
}
|
||||
const Type* ModFloatingNode::Value(PhaseGVN* phase) const {
|
||||
const Type* t = CallLeafPureNode::Value(phase);
|
||||
if (t == Type::TOP) { return Type::TOP; }
|
||||
const Type* dividend_type = phase->type(dividend());
|
||||
const Type* divisor_type = phase->type(divisor());
|
||||
if (dividend_type == Type::TOP || divisor_type == Type::TOP) {
|
||||
return Type::TOP;
|
||||
}
|
||||
|
||||
return CallLeafPureNode::Ideal(phase, can_reshape);
|
||||
}
|
||||
|
||||
/* Give a tuple node for ::Ideal to return, made of the input state (control to return addr)
|
||||
* and the given constant result. Idealization of projections will make sure to transparently
|
||||
* propagate the input state and replace the result by the said constant.
|
||||
*/
|
||||
TupleNode* ModFloatingNode::make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const {
|
||||
Node* con_node = phase->makecon(con);
|
||||
TupleNode* tuple = TupleNode::make(
|
||||
tf()->range(),
|
||||
in(TypeFunc::Control),
|
||||
in(TypeFunc::I_O),
|
||||
in(TypeFunc::Memory),
|
||||
in(TypeFunc::FramePtr),
|
||||
in(TypeFunc::ReturnAdr),
|
||||
con_node);
|
||||
|
||||
return tuple;
|
||||
const Type* constant_result = get_result_if_constant(dividend_type, divisor_type);
|
||||
if (constant_result != nullptr) {
|
||||
const TypeTuple* tt = t->is_tuple();
|
||||
uint cnt = tt->cnt();
|
||||
uint param_cnt = cnt - TypeFunc::Parms;
|
||||
const Type** fields = TypeTuple::fields(param_cnt);
|
||||
fields[TypeFunc::Parms] = constant_result;
|
||||
if (param_cnt > 1) { fields[TypeFunc::Parms + 1] = Type::HALF; }
|
||||
return TypeTuple::make(cnt, fields);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@ -175,8 +175,6 @@ public:
|
||||
|
||||
// Base class for float and double modulus
|
||||
class ModFloatingNode : public CallLeafPureNode {
|
||||
TupleNode* make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const;
|
||||
|
||||
protected:
|
||||
virtual Node* dividend() const = 0;
|
||||
virtual Node* divisor() const = 0;
|
||||
@ -184,7 +182,7 @@ protected:
|
||||
|
||||
public:
|
||||
ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name);
|
||||
Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
|
||||
const Type* Value(PhaseGVN* phase) const override;
|
||||
};
|
||||
|
||||
// Float Modulus
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,7 @@ import compiler.lib.ir_framework.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8345766
|
||||
* @bug 8345766 8378742
|
||||
* @key randomness
|
||||
* @summary Test that Ideal transformations of ModDNode are being performed as expected.
|
||||
* @library /test/lib /
|
||||
@ -37,6 +37,7 @@ import compiler.lib.ir_framework.*;
|
||||
*/
|
||||
public class ModDNodeTests {
|
||||
public static final double q = Utils.getRandomInstance().nextDouble() * 100.0d;
|
||||
public static volatile int volatileField;
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
@ -48,6 +49,7 @@ public class ModDNodeTests {
|
||||
"unusedResultAfterLoopOpt1",
|
||||
"unusedResultAfterLoopOpt2",
|
||||
"unusedResultAfterLoopOpt3",
|
||||
"constantFoldInCCP"
|
||||
})
|
||||
public void runMethod() {
|
||||
Asserts.assertEQ(constant(), q % 72.0d % 30.0d);
|
||||
@ -61,6 +63,7 @@ public class ModDNodeTests {
|
||||
Asserts.assertEQ(unusedResultAfterLoopOpt1(1.1d, 2.2d), 0.d);
|
||||
Asserts.assertEQ(unusedResultAfterLoopOpt2(1.1d, 2.2d), 0.d);
|
||||
Asserts.assertEQ(unusedResultAfterLoopOpt3(1.1d, 2.2d), 0.d);
|
||||
Asserts.assertEQ(constantFoldInCCP(), 4.0d);
|
||||
}
|
||||
|
||||
// Note: we used to check for ConD nodes in the IR. But that is a bit brittle:
|
||||
@ -72,7 +75,7 @@ public class ModDNodeTests {
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_D, "2"},
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double constant() {
|
||||
// All constants available during parsing
|
||||
@ -84,7 +87,7 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "1"},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double alsoConstant() {
|
||||
// Make sure value is only available after second loop opts round
|
||||
@ -102,7 +105,7 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "2"},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double nanLeftConstant() {
|
||||
// Make sure value is only available after second loop opts round
|
||||
@ -120,7 +123,7 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "2"},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double nanRightConstant() {
|
||||
// Make sure value is only available after second loop opts round
|
||||
@ -156,7 +159,7 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "0"},
|
||||
phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public void unusedResult(double x, double y) {
|
||||
double unused = x % y;
|
||||
@ -165,9 +168,9 @@ public class ModDNodeTests {
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_D, "1"},
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "0"},
|
||||
@IR(failOn = {IRNode.MOD_D},
|
||||
phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public void repeatedlyUnused(double x, double y) {
|
||||
double unused = 1.d;
|
||||
@ -185,9 +188,9 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "1"},
|
||||
phase = CompilePhase.ITER_GVN2)
|
||||
@IR(counts = {IRNode.MOD_D, "0"},
|
||||
@IR(failOn = {IRNode.MOD_D},
|
||||
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double unusedResultAfterLoopOpt1(double x, double y) {
|
||||
double unused = x % y;
|
||||
@ -210,9 +213,9 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "1"},
|
||||
phase = CompilePhase.AFTER_CLOOPS)
|
||||
@IR(counts = {IRNode.MOD_D, "0"},
|
||||
@IR(failOn = {IRNode.MOD_D},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1)
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double unusedResultAfterLoopOpt2(double x, double y) {
|
||||
int a = 77;
|
||||
@ -235,9 +238,9 @@ public class ModDNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_D, "2"},
|
||||
phase = CompilePhase.AFTER_CLOOPS) // drop the useless one
|
||||
@IR(counts = {IRNode.MOD_D, "0"},
|
||||
@IR(failOn = {IRNode.MOD_D},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // drop the rest
|
||||
@IR(counts = {".*CallLeaf.*drem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*drem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public double unusedResultAfterLoopOpt3(double x, double y) {
|
||||
double unused = x % y;
|
||||
@ -252,4 +255,25 @@ public class ModDNodeTests {
|
||||
int other = (b - 77) * (int)(x % y % 1.d);
|
||||
return (double)other;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.CMP_D},
|
||||
phase = CompilePhase.CCP1)
|
||||
@IR(failOn = {IRNode.MOD_D},
|
||||
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
|
||||
public double constantFoldInCCP(){
|
||||
int i;
|
||||
for (i = 2; i < 4; i *= 2) {
|
||||
}
|
||||
int j;
|
||||
for (j = 2; j < 4; j *= 2) {
|
||||
}
|
||||
volatileField = 42;
|
||||
double v1 = (double) i / 2;
|
||||
double v2 = j;
|
||||
double v = v1 % v2;
|
||||
for (; v < v2; v *= 2) {
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,7 @@ import compiler.lib.ir_framework.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8345766
|
||||
* @bug 8345766 8378742
|
||||
* @key randomness
|
||||
* @summary Test that Ideal transformations of ModFNode are being performed as expected.
|
||||
* @library /test/lib /
|
||||
@ -37,6 +37,7 @@ import compiler.lib.ir_framework.*;
|
||||
*/
|
||||
public class ModFNodeTests {
|
||||
public static final float q = Utils.getRandomInstance().nextFloat() * 100.0f;
|
||||
public static volatile int volatileField;
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
@ -48,6 +49,7 @@ public class ModFNodeTests {
|
||||
"unusedResultAfterLoopOpt1",
|
||||
"unusedResultAfterLoopOpt2",
|
||||
"unusedResultAfterLoopOpt3",
|
||||
"constantFoldInCCP"
|
||||
})
|
||||
public void runMethod() {
|
||||
Asserts.assertEQ(constant(), q % 72.0f % 30.0f);
|
||||
@ -61,6 +63,7 @@ public class ModFNodeTests {
|
||||
Asserts.assertEQ(unusedResultAfterLoopOpt1(1.1f, 2.2f), 0.f);
|
||||
Asserts.assertEQ(unusedResultAfterLoopOpt2(1.1f, 2.2f), 0.f);
|
||||
Asserts.assertEQ(unusedResultAfterLoopOpt3(1.1f, 2.2f), 0.f);
|
||||
Asserts.assertEQ(constantFoldInCCP(), 4.0f);
|
||||
}
|
||||
|
||||
// Note: we used to check for ConF nodes in the IR. But that is a bit brittle:
|
||||
@ -72,7 +75,7 @@ public class ModFNodeTests {
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_F, "2"},
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float constant() {
|
||||
// All constants available during parsing
|
||||
@ -84,7 +87,7 @@ public class ModFNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "1"},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float alsoConstant() {
|
||||
// Make sure value is only available after second loop opts round
|
||||
@ -102,7 +105,7 @@ public class ModFNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "2"},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float nanLeftConstant() {
|
||||
// Make sure value is only available after second loop opts round
|
||||
@ -120,7 +123,7 @@ public class ModFNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "2"},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // Only constant fold after some loop opts
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float nanRightConstant() {
|
||||
// Make sure value is only available after second loop opts round
|
||||
@ -154,9 +157,9 @@ public class ModFNodeTests {
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_F, "1"},
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "0"},
|
||||
@IR(failOn = {IRNode.MOD_F},
|
||||
phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public void unusedResult(float x, float y) {
|
||||
float unused = x % y;
|
||||
@ -165,9 +168,9 @@ public class ModFNodeTests {
|
||||
@Test
|
||||
@IR(counts = {IRNode.MOD_F, "1"},
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "0"},
|
||||
@IR(failOn = {IRNode.MOD_F},
|
||||
phase = CompilePhase.ITER_GVN1) // IGVN removes unused nodes
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public void repeatedlyUnused(float x, float y) {
|
||||
float unused = 1.f;
|
||||
@ -185,9 +188,9 @@ public class ModFNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "1"},
|
||||
phase = CompilePhase.ITER_GVN2)
|
||||
@IR(counts = {IRNode.MOD_F, "0"},
|
||||
@IR(failOn = {IRNode.MOD_F},
|
||||
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float unusedResultAfterLoopOpt1(float x, float y) {
|
||||
float unused = x % y;
|
||||
@ -210,9 +213,9 @@ public class ModFNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "1"},
|
||||
phase = CompilePhase.AFTER_CLOOPS)
|
||||
@IR(counts = {IRNode.MOD_F, "0"},
|
||||
@IR(failOn = {IRNode.MOD_F},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1)
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float unusedResultAfterLoopOpt2(float x, float y) {
|
||||
int a = 77;
|
||||
@ -235,9 +238,9 @@ public class ModFNodeTests {
|
||||
phase = CompilePhase.AFTER_PARSING)
|
||||
@IR(counts = {IRNode.MOD_F, "2"},
|
||||
phase = CompilePhase.AFTER_CLOOPS) // drop the useless one
|
||||
@IR(counts = {IRNode.MOD_F, "0"},
|
||||
@IR(failOn = {IRNode.MOD_F},
|
||||
phase = CompilePhase.PHASEIDEALLOOP1) // drop the rest
|
||||
@IR(counts = {".*CallLeaf.*frem.*", "0"},
|
||||
@IR(failOn = {".*CallLeaf.*frem.*"},
|
||||
phase = CompilePhase.BEFORE_MATCHING)
|
||||
public float unusedResultAfterLoopOpt3(float x, float y) {
|
||||
float unused = x % y;
|
||||
@ -252,4 +255,25 @@ public class ModFNodeTests {
|
||||
int other = (b - 77) * (int)(x % y % 1.f);
|
||||
return (float)other;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.CMP_F},
|
||||
phase = CompilePhase.CCP1)
|
||||
@IR(failOn = {IRNode.MOD_F},
|
||||
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
|
||||
public float constantFoldInCCP(){
|
||||
int i;
|
||||
for (i = 2; i < 4; i *= 2) {
|
||||
}
|
||||
int j;
|
||||
for (j = 2; j < 4; j *= 2) {
|
||||
}
|
||||
volatileField = 42;
|
||||
float v1 = (float) i / 2;
|
||||
float v2 = j;
|
||||
float v = v1 % v2;
|
||||
for (; v < v2; v *= 2) {
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user