Skip to content

Commit ed70910

Browse files
8347901: C2 should remove unused leaf / pure runtime calls
Reviewed-by: thartmann, vlivanov
1 parent f155661 commit ed70910

File tree

15 files changed

+282
-145
lines changed

15 files changed

+282
-145
lines changed

src/hotspot/share/opto/callnode.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ Node *CallNode::result_cast() {
918918
}
919919

920920

921-
void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) {
921+
void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) const {
922922
projs->fallthrough_proj = nullptr;
923923
projs->fallthrough_catchproj = nullptr;
924924
projs->fallthrough_ioproj = nullptr;
@@ -1303,6 +1303,57 @@ void CallLeafVectorNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_
13031303

13041304

13051305
//=============================================================================
1306+
bool CallLeafPureNode::is_unused() const {
1307+
return proj_out_or_null(TypeFunc::Parms) == nullptr;
1308+
}
1309+
1310+
bool CallLeafPureNode::is_dead() const {
1311+
return proj_out_or_null(TypeFunc::Control) == nullptr;
1312+
}
1313+
1314+
/* We make a tuple of the global input state + TOP for the output values.
1315+
* We use this to delete a pure function that is not used: by replacing the call with
1316+
* such a tuple, we let output Proj's idealization pick the corresponding input of the
1317+
* pure call, so jumping over it, and effectively, removing the call from the graph.
1318+
* This avoids doing the graph surgery manually, but leaves that to IGVN
1319+
* that is specialized for doing that right. We need also tuple components for output
1320+
* values of the function to respect the return arity, and in case there is a projection
1321+
* that would pick an output (which shouldn't happen at the moment).
1322+
*/
1323+
TupleNode* CallLeafPureNode::make_tuple_of_input_state_and_top_return_values(const Compile* C) const {
1324+
// Transparently propagate input state but parameters
1325+
TupleNode* tuple = TupleNode::make(
1326+
tf()->range(),
1327+
in(TypeFunc::Control),
1328+
in(TypeFunc::I_O),
1329+
in(TypeFunc::Memory),
1330+
in(TypeFunc::FramePtr),
1331+
in(TypeFunc::ReturnAdr));
1332+
1333+
// And add TOPs for the return values
1334+
for (uint i = TypeFunc::Parms; i < tf()->range()->cnt(); i++) {
1335+
tuple->set_req(i, C->top());
1336+
}
1337+
1338+
return tuple;
1339+
}
1340+
1341+
Node* CallLeafPureNode::Ideal(PhaseGVN* phase, bool can_reshape) {
1342+
if (is_dead()) {
1343+
return nullptr;
1344+
}
1345+
1346+
// We need to wait until IGVN because during parsing, usages might still be missing
1347+
// and we would remove the call immediately.
1348+
if (can_reshape && is_unused()) {
1349+
// The result is not used. We remove the call by replacing it with a tuple, that
1350+
// is later disintegrated by the projections.
1351+
return make_tuple_of_input_state_and_top_return_values(phase->C);
1352+
}
1353+
1354+
return CallRuntimeNode::Ideal(phase, can_reshape);
1355+
}
1356+
13061357
#ifndef PRODUCT
13071358
void CallLeafNode::dump_spec(outputStream *st) const {
13081359
st->print("# ");

src/hotspot/share/opto/callnode.hpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ class CallNode : public SafePointNode {
738738
// Collect all the interesting edges from a call for use in
739739
// replacing the call by something else. Used by macro expansion
740740
// and the late inlining support.
741-
void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true);
741+
void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true) const;
742742

743743
virtual uint match_edge(uint idx) const;
744744

@@ -914,6 +914,33 @@ class CallLeafNode : public CallRuntimeNode {
914914
#endif
915915
};
916916

917+
/* A pure function call, they are assumed not to be safepoints, not to read or write memory,
918+
* have no exception... They just take parameters, return a value without side effect. It is
919+
* always correct to create some, or remove them, if the result is not used.
920+
*
921+
* They still have control input to allow easy lowering into other kind of calls that require
922+
* a control, but this is more a technical than a moral constraint.
923+
*
924+
* Pure calls must have only control and data input and output: I/O, Memory and so on must be top.
925+
* Nevertheless, pure calls can typically be expensive math operations so care must be taken
926+
* when letting the node float.
927+
*/
928+
class CallLeafPureNode : public CallLeafNode {
929+
protected:
930+
bool is_unused() const;
931+
bool is_dead() const;
932+
TupleNode* make_tuple_of_input_state_and_top_return_values(const Compile* C) const;
933+
934+
public:
935+
CallLeafPureNode(const TypeFunc* tf, address addr, const char* name,
936+
const TypePtr* adr_type)
937+
: CallLeafNode(tf, addr, name, adr_type) {
938+
init_class_id(Class_CallLeafPure);
939+
}
940+
int Opcode() const override;
941+
Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
942+
};
943+
917944
//------------------------------CallLeafNoFPNode-------------------------------
918945
// CallLeafNode, not using floating point or using it in the same manner as
919946
// the generated code

src/hotspot/share/opto/classes.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ macro(CallDynamicJava)
6161
macro(CallJava)
6262
macro(CallLeaf)
6363
macro(CallLeafNoFP)
64+
macro(CallLeafPure)
6465
macro(CallLeafVector)
6566
macro(CallRuntime)
6667
macro(CallStaticJava)
@@ -372,6 +373,7 @@ macro(SubI)
372373
macro(SubL)
373374
macro(TailCall)
374375
macro(TailJump)
376+
macro(Tuple)
375377
macro(MacroLogicV)
376378
macro(ThreadLocal)
377379
macro(Unlock)

src/hotspot/share/opto/compile.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,6 +3303,25 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
33033303
case Op_Opaque1: // Remove Opaque Nodes before matching
33043304
n->subsume_by(n->in(1), this);
33053305
break;
3306+
case Op_CallLeafPure: {
3307+
// If the pure call is not supported, then lower to a CallLeaf.
3308+
if (!Matcher::match_rule_supported(Op_CallLeafPure)) {
3309+
CallNode* call = n->as_Call();
3310+
CallNode* new_call = new CallLeafNode(call->tf(), call->entry_point(),
3311+
call->_name, TypeRawPtr::BOTTOM);
3312+
new_call->init_req(TypeFunc::Control, call->in(TypeFunc::Control));
3313+
new_call->init_req(TypeFunc::I_O, C->top());
3314+
new_call->init_req(TypeFunc::Memory, C->top());
3315+
new_call->init_req(TypeFunc::ReturnAdr, C->top());
3316+
new_call->init_req(TypeFunc::FramePtr, C->top());
3317+
for (unsigned int i = TypeFunc::Parms; i < call->tf()->domain()->cnt(); i++) {
3318+
new_call->init_req(i, call->in(i));
3319+
}
3320+
n->subsume_by(new_call, this);
3321+
}
3322+
frc.inc_call_count();
3323+
break;
3324+
}
33063325
case Op_CallStaticJava:
33073326
case Op_CallJava:
33083327
case Op_CallDynamicJava:

src/hotspot/share/opto/divnode.cpp

Lines changed: 65 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,19 @@
4242

4343
#include <math.h>
4444

45-
ModFloatingNode::ModFloatingNode(Compile* C, const TypeFunc* tf, const char* name) : CallLeafNode(tf, nullptr, name, TypeRawPtr::BOTTOM) {
45+
ModFloatingNode::ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name) : CallLeafPureNode(tf, addr, name, TypeRawPtr::BOTTOM) {
4646
add_flag(Flag_is_macro);
4747
C->add_macro_node(this);
4848
}
4949

50-
ModDNode::ModDNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::Math_DD_D_Type(), "drem") {
50+
ModDNode::ModDNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::drem), "drem") {
5151
init_req(TypeFunc::Parms + 0, a);
5252
init_req(TypeFunc::Parms + 1, C->top());
5353
init_req(TypeFunc::Parms + 2, b);
5454
init_req(TypeFunc::Parms + 3, C->top());
5555
}
5656

57-
ModFNode::ModFNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::modf_Type(), "frem") {
57+
ModFNode::ModFNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::modf_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::frem), "frem") {
5858
init_req(TypeFunc::Parms + 0, a);
5959
init_req(TypeFunc::Parms + 1, b);
6060
}
@@ -1511,137 +1511,109 @@ const Type* UModLNode::Value(PhaseGVN* phase) const {
15111511
return unsigned_mod_value<TypeLong, julong, jlong>(phase, this);
15121512
}
15131513

1514-
Node* ModFNode::Ideal(PhaseGVN* phase, bool can_reshape) {
1515-
if (!can_reshape) {
1516-
return nullptr;
1517-
}
1518-
PhaseIterGVN* igvn = phase->is_IterGVN();
1519-
1520-
bool result_is_unused = proj_out_or_null(TypeFunc::Parms) == nullptr;
1521-
bool not_dead = proj_out_or_null(TypeFunc::Control) != nullptr;
1522-
if (result_is_unused && not_dead) {
1523-
return replace_with_con(igvn, TypeF::make(0.));
1524-
}
1525-
1526-
// Either input is TOP ==> the result is TOP
1527-
const Type* t1 = phase->type(dividend());
1528-
const Type* t2 = phase->type(divisor());
1529-
if (t1 == Type::TOP || t2 == Type::TOP) {
1530-
return phase->C->top();
1531-
}
1532-
1514+
const Type* ModFNode::get_result_if_constant(const Type* dividend, const Type* divisor) const {
15331515
// If either number is not a constant, we know nothing.
1534-
if ((t1->base() != Type::FloatCon) || (t2->base() != Type::FloatCon)) {
1516+
if ((dividend->base() != Type::FloatCon) || (divisor->base() != Type::FloatCon)) {
15351517
return nullptr; // note: x%x can be either NaN or 0
15361518
}
15371519

1538-
float f1 = t1->getf();
1539-
float f2 = t2->getf();
1540-
jint x1 = jint_cast(f1); // note: *(int*)&f1, not just (int)f1
1541-
jint x2 = jint_cast(f2);
1520+
float dividend_f = dividend->getf();
1521+
float divisor_f = divisor->getf();
1522+
jint dividend_i = jint_cast(dividend_f); // note: *(int*)&f1, not just (int)f1
1523+
jint divisor_i = jint_cast(divisor_f);
15421524

15431525
// If either is a NaN, return an input NaN
1544-
if (g_isnan(f1)) {
1545-
return replace_with_con(igvn, t1);
1526+
if (g_isnan(dividend_f)) {
1527+
return dividend;
15461528
}
1547-
if (g_isnan(f2)) {
1548-
return replace_with_con(igvn, t2);
1529+
if (g_isnan(divisor_f)) {
1530+
return divisor;
15491531
}
15501532

15511533
// If an operand is infinity or the divisor is +/- zero, punt.
1552-
if (!g_isfinite(f1) || !g_isfinite(f2) || x2 == 0 || x2 == min_jint) {
1534+
if (!g_isfinite(dividend_f) || !g_isfinite(divisor_f) || divisor_i == 0 || divisor_i == min_jint) {
15531535
return nullptr;
15541536
}
15551537

15561538
// We must be modulo'ing 2 float constants.
15571539
// Make sure that the sign of the fmod is equal to the sign of the dividend
1558-
jint xr = jint_cast(fmod(f1, f2));
1559-
if ((x1 ^ xr) < 0) {
1540+
jint xr = jint_cast(fmod(dividend_f, divisor_f));
1541+
if ((dividend_i ^ xr) < 0) {
15601542
xr ^= min_jint;
15611543
}
15621544

1563-
return replace_with_con(igvn, TypeF::make(jfloat_cast(xr)));
1545+
return TypeF::make(jfloat_cast(xr));
15641546
}
15651547

1566-
Node* ModDNode::Ideal(PhaseGVN* phase, bool can_reshape) {
1567-
if (!can_reshape) {
1568-
return nullptr;
1569-
}
1570-
PhaseIterGVN* igvn = phase->is_IterGVN();
1571-
1572-
bool result_is_unused = proj_out_or_null(TypeFunc::Parms) == nullptr;
1573-
bool not_dead = proj_out_or_null(TypeFunc::Control) != nullptr;
1574-
if (result_is_unused && not_dead) {
1575-
return replace_with_con(igvn, TypeD::make(0.));
1576-
}
1577-
1578-
// Either input is TOP ==> the result is TOP
1579-
const Type* t1 = phase->type(dividend());
1580-
const Type* t2 = phase->type(divisor());
1581-
if (t1 == Type::TOP || t2 == Type::TOP) {
1582-
return nullptr;
1583-
}
1584-
1548+
const Type* ModDNode::get_result_if_constant(const Type* dividend, const Type* divisor) const {
15851549
// If either number is not a constant, we know nothing.
1586-
if ((t1->base() != Type::DoubleCon) || (t2->base() != Type::DoubleCon)) {
1550+
if ((dividend->base() != Type::DoubleCon) || (divisor->base() != Type::DoubleCon)) {
15871551
return nullptr; // note: x%x can be either NaN or 0
15881552
}
15891553

1590-
double f1 = t1->getd();
1591-
double f2 = t2->getd();
1592-
jlong x1 = jlong_cast(f1); // note: *(long*)&f1, not just (long)f1
1593-
jlong x2 = jlong_cast(f2);
1554+
double dividend_d = dividend->getd();
1555+
double divisor_d = divisor->getd();
1556+
jlong dividend_l = jlong_cast(dividend_d); // note: *(long*)&f1, not just (long)f1
1557+
jlong divisor_l = jlong_cast(divisor_d);
15941558

15951559
// If either is a NaN, return an input NaN
1596-
if (g_isnan(f1)) {
1597-
return replace_with_con(igvn, t1);
1560+
if (g_isnan(dividend_d)) {
1561+
return dividend;
15981562
}
1599-
if (g_isnan(f2)) {
1600-
return replace_with_con(igvn, t2);
1563+
if (g_isnan(divisor_d)) {
1564+
return divisor;
16011565
}
16021566

16031567
// If an operand is infinity or the divisor is +/- zero, punt.
1604-
if (!g_isfinite(f1) || !g_isfinite(f2) || x2 == 0 || x2 == min_jlong) {
1568+
if (!g_isfinite(dividend_d) || !g_isfinite(divisor_d) || divisor_l == 0 || divisor_l == min_jlong) {
16051569
return nullptr;
16061570
}
16071571

16081572
// We must be modulo'ing 2 double constants.
16091573
// Make sure that the sign of the fmod is equal to the sign of the dividend
1610-
jlong xr = jlong_cast(fmod(f1, f2));
1611-
if ((x1 ^ xr) < 0) {
1574+
jlong xr = jlong_cast(fmod(dividend_d, divisor_d));
1575+
if ((dividend_l ^ xr) < 0) {
16121576
xr ^= min_jlong;
16131577
}
16141578

1615-
return replace_with_con(igvn, TypeD::make(jdouble_cast(xr)));
1579+
return TypeD::make(jdouble_cast(xr));
16161580
}
16171581

1618-
Node* ModFloatingNode::replace_with_con(PhaseIterGVN* phase, const Type* con) {
1619-
Compile* C = phase->C;
1620-
Node* con_node = phase->makecon(con);
1621-
CallProjections projs;
1622-
extract_projections(&projs, false, false);
1623-
phase->replace_node(projs.fallthrough_proj, in(TypeFunc::Control));
1624-
if (projs.fallthrough_catchproj != nullptr) {
1625-
phase->replace_node(projs.fallthrough_catchproj, in(TypeFunc::Control));
1626-
}
1627-
if (projs.fallthrough_memproj != nullptr) {
1628-
phase->replace_node(projs.fallthrough_memproj, in(TypeFunc::Memory));
1629-
}
1630-
if (projs.catchall_memproj != nullptr) {
1631-
phase->replace_node(projs.catchall_memproj, C->top());
1632-
}
1633-
if (projs.fallthrough_ioproj != nullptr) {
1634-
phase->replace_node(projs.fallthrough_ioproj, in(TypeFunc::I_O));
1635-
}
1636-
assert(projs.catchall_ioproj == nullptr, "no exceptions from floating mod");
1637-
assert(projs.catchall_catchproj == nullptr, "no exceptions from floating mod");
1638-
if (projs.resproj != nullptr) {
1639-
phase->replace_node(projs.resproj, con_node);
1582+
Node* ModFloatingNode::Ideal(PhaseGVN* phase, bool can_reshape) {
1583+
if (can_reshape) {
1584+
PhaseIterGVN* igvn = phase->is_IterGVN();
1585+
1586+
// Either input is TOP ==> the result is TOP
1587+
const Type* dividend_type = phase->type(dividend());
1588+
const Type* divisor_type = phase->type(divisor());
1589+
if (dividend_type == Type::TOP || divisor_type == Type::TOP) {
1590+
return phase->C->top();
1591+
}
1592+
const Type* constant_result = get_result_if_constant(dividend_type, divisor_type);
1593+
if (constant_result != nullptr) {
1594+
return make_tuple_of_input_state_and_constant_result(igvn, constant_result);
1595+
}
16401596
}
1641-
phase->replace_node(this, C->top());
1642-
C->remove_macro_node(this);
1643-
disconnect_inputs(C);
1644-
return nullptr;
1597+
1598+
return CallLeafPureNode::Ideal(phase, can_reshape);
1599+
}
1600+
1601+
/* Give a tuple node for ::Ideal to return, made of the input state (control to return addr)
1602+
* and the given constant result. Idealization of projections will make sure to transparently
1603+
* propagate the input state and replace the result by the said constant.
1604+
*/
1605+
TupleNode* ModFloatingNode::make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const {
1606+
Node* con_node = phase->makecon(con);
1607+
TupleNode* tuple = TupleNode::make(
1608+
tf()->range(),
1609+
in(TypeFunc::Control),
1610+
in(TypeFunc::I_O),
1611+
in(TypeFunc::Memory),
1612+
in(TypeFunc::FramePtr),
1613+
in(TypeFunc::ReturnAdr),
1614+
con_node);
1615+
1616+
return tuple;
16451617
}
16461618

16471619
//=============================================================================

0 commit comments

Comments
 (0)