Richard Biener <richard.guent...@gmail.com> writes: > On Wed, Nov 10, 2021 at 1:46 PM Richard Sandiford via Gcc-patches > <gcc-patches@gcc.gnu.org> wrote: >> >> code_helper and gimple_match_op seem like generally useful ways >> of summing up a gimple_assign or gimple_call (or gimple_cond). >> This patch adds a gimple_extract_op function that can be used >> for that. >> >> Tested on aarch64-linux-gnu and x86_64-linux-gnu. OK to install? >> >> Richard >> >> >> gcc/ >> * gimple-match.h (gimple_extract_op): Declare. >> * gimple-match.c (gimple_extract): New function, extracted from... >> (gimple_simplify): ...here. >> (gimple_extract_op): New function. >> --- >> gcc/gimple-match-head.c | 261 +++++++++++++++++++++++----------------- >> gcc/gimple-match.h | 1 + >> 2 files changed, 149 insertions(+), 113 deletions(-) >> >> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c >> index 9d88b2f8551..4c6e0883ba4 100644 >> --- a/gcc/gimple-match-head.c >> +++ b/gcc/gimple-match-head.c >> @@ -890,12 +890,29 @@ try_conditional_simplification (internal_fn ifn, >> gimple_match_op *res_op, >> return true; >> } >> >> -/* The main STMT based simplification entry. It is used by the fold_stmt >> - and the fold_stmt_to_constant APIs. */ >> +/* Common subroutine of gimple_extract_op and gimple_simplify. Try to >> + describe STMT in RES_OP. Return: >> >> -bool >> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq, >> - tree (*valueize)(tree), tree (*top_valueize)(tree)) >> + - -1 if extraction failed >> + - otherwise, 0 if no simplification should take place >> + - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND >> + - otherwise, -2 for a GIMPLE_CALL >> + >> + Before recording an operand, call: >> + >> + - VALUEIZE_CONDITION for a COND_EXPR condition >> + - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME > > I think at least VALUEIZE_NAME is unnecessary, see below
Yeah, it's unnecessary. The idea was to (try to) ensure that gimple_simplify keeps all the microoptimisations that it had previously. This includes open-coding do_valueize for SSA_NAMEs and jumping straight to the right gimplify_resimplifyN routine when the number of operands is already known. (The two calls to gimple_extract<> produce different functions that ought to get inlined into their single callers. A lot of the jumps should then be threaded.) I can drop all that if you don't think it's worth it though. Just wanted to double-check first. Thanks, Richard >> + - VALUEIZE_OP for every other top-level operand >> + >> + Each routine takes a tree argument and returns a tree. */ >> + >> +template<typename ValueizeOp, typename ValueizeCondition, >> + typename ValueizeName> >> +inline int >> +gimple_extract (gimple *stmt, gimple_match_op *res_op, >> + ValueizeOp valueize_op, >> + ValueizeCondition valueize_condition, >> + ValueizeName valueize_name) >> { >> switch (gimple_code (stmt)) >> { >> @@ -911,100 +928,53 @@ gimple_simplify (gimple *stmt, gimple_match_op >> *res_op, gimple_seq *seq, >> || code == VIEW_CONVERT_EXPR) >> { >> tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0); >> - bool valueized = false; >> - op0 = do_valueize (op0, top_valueize, valueized); >> - res_op->set_op (code, type, op0); >> - return (gimple_resimplify1 (seq, res_op, valueize) >> - || valueized); >> + res_op->set_op (code, type, valueize_op (op0)); >> + return 1; >> } >> else if (code == BIT_FIELD_REF) >> { >> tree rhs1 = gimple_assign_rhs1 (stmt); >> - tree op0 = TREE_OPERAND (rhs1, 0); >> - bool valueized = false; >> - op0 = do_valueize (op0, top_valueize, valueized); >> + tree op0 = valueize_op (TREE_OPERAND (rhs1, 0)); >> res_op->set_op (code, type, op0, >> TREE_OPERAND (rhs1, 1), >> TREE_OPERAND (rhs1, 2), >> REF_REVERSE_STORAGE_ORDER (rhs1)); >> - if (res_op->reverse) >> - return valueized; >> - return (gimple_resimplify3 (seq, res_op, valueize) >> - || valueized); >> + return res_op->reverse ? 0 : 3; >> } >> - else if (code == SSA_NAME >> - && top_valueize) >> + else if (code == SSA_NAME) >> { >> tree op0 = gimple_assign_rhs1 (stmt); >> - tree valueized = top_valueize (op0); >> + tree valueized = valueize_name (op0); >> if (!valueized || op0 == valueized) >> - return false; >> + return -1; >> res_op->set_op (TREE_CODE (op0), type, valueized); >> - return true; >> + return 0; > > So the old code in an obfuscated way just knowed nothing simplifies > on the plain not valueized name but returned true when valueization > changed the stmt. So I'd expect > > tree valueized = valueize_op (op0); > res_op->set_op (TREE_CODE (op0), type, valueized); > return 0; > > here and the gimple_simplify caller returning 'valueized'. I think > that the old code treated a NULL top_valueize () as "fail" is just > premature optimization without any effect. > >> } >> break; >> case GIMPLE_UNARY_RHS: >> { >> tree rhs1 = gimple_assign_rhs1 (stmt); >> - bool valueized = false; >> - rhs1 = do_valueize (rhs1, top_valueize, valueized); >> - res_op->set_op (code, type, rhs1); >> - return (gimple_resimplify1 (seq, res_op, valueize) >> - || valueized); >> + res_op->set_op (code, type, valueize_op (rhs1)); >> + return 1; >> } >> case GIMPLE_BINARY_RHS: >> { >> - tree rhs1 = gimple_assign_rhs1 (stmt); >> - tree rhs2 = gimple_assign_rhs2 (stmt); >> - bool valueized = false; >> - rhs1 = do_valueize (rhs1, top_valueize, valueized); >> - rhs2 = do_valueize (rhs2, top_valueize, valueized); >> + tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt)); >> + tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt)); >> res_op->set_op (code, type, rhs1, rhs2); >> - return (gimple_resimplify2 (seq, res_op, valueize) >> - || valueized); >> + return 2; >> } >> case GIMPLE_TERNARY_RHS: >> { >> - bool valueized = false; >> tree rhs1 = gimple_assign_rhs1 (stmt); >> - /* If this is a COND_EXPR first try to simplify an >> - embedded GENERIC condition. */ >> - if (code == COND_EXPR) >> - { >> - if (COMPARISON_CLASS_P (rhs1)) >> - { >> - tree lhs = TREE_OPERAND (rhs1, 0); >> - tree rhs = TREE_OPERAND (rhs1, 1); >> - lhs = do_valueize (lhs, top_valueize, valueized); >> - rhs = do_valueize (rhs, top_valueize, valueized); >> - gimple_match_op res_op2 (res_op->cond, TREE_CODE >> (rhs1), >> - TREE_TYPE (rhs1), lhs, rhs); >> - if ((gimple_resimplify2 (seq, &res_op2, valueize) >> - || valueized) >> - && res_op2.code.is_tree_code ()) >> - { >> - valueized = true; >> - if (TREE_CODE_CLASS ((enum tree_code) res_op2.code) >> - == tcc_comparison) >> - rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1), >> - res_op2.ops[0], res_op2.ops[1]); >> - else if (res_op2.code == SSA_NAME >> - || res_op2.code == INTEGER_CST >> - || res_op2.code == VECTOR_CST) >> - rhs1 = res_op2.ops[0]; >> - else >> - valueized = false; >> - } >> - } >> - } >> - tree rhs2 = gimple_assign_rhs2 (stmt); >> - tree rhs3 = gimple_assign_rhs3 (stmt); >> - rhs1 = do_valueize (rhs1, top_valueize, valueized); >> - rhs2 = do_valueize (rhs2, top_valueize, valueized); >> - rhs3 = do_valueize (rhs3, top_valueize, valueized); >> + if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1)) >> + rhs1 = valueize_condition (rhs1); >> + else >> + rhs1 = valueize_op (rhs1); >> + tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt)); >> + tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt)); >> res_op->set_op (code, type, rhs1, rhs2, rhs3); >> - return (gimple_resimplify3 (seq, res_op, valueize) >> - || valueized); >> + return 3; >> } >> default: >> gcc_unreachable (); >> @@ -1018,7 +988,6 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, >> gimple_seq *seq, >> && gimple_call_num_args (stmt) >= 1 >> && gimple_call_num_args (stmt) <= 5) >> { >> - bool valueized = false; >> combined_fn cfn; >> if (gimple_call_internal_p (stmt)) >> cfn = as_combined_fn (gimple_call_internal_fn (stmt)); >> @@ -1026,17 +995,17 @@ gimple_simplify (gimple *stmt, gimple_match_op >> *res_op, gimple_seq *seq, >> { >> tree fn = gimple_call_fn (stmt); >> if (!fn) >> - return false; >> + return -1; >> >> - fn = do_valueize (fn, top_valueize, valueized); >> + fn = valueize_op (fn); >> if (TREE_CODE (fn) != ADDR_EXPR >> || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) >> - return false; >> + return -1; >> >> tree decl = TREE_OPERAND (fn, 0); >> if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL >> || !gimple_builtin_call_types_compatible_p (stmt, decl)) >> - return false; >> + return -1; >> >> cfn = as_combined_fn (DECL_FUNCTION_CODE (decl)); >> } >> @@ -1044,56 +1013,122 @@ gimple_simplify (gimple *stmt, gimple_match_op >> *res_op, gimple_seq *seq, >> unsigned int num_args = gimple_call_num_args (stmt); >> res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args); >> for (unsigned i = 0; i < num_args; ++i) >> - { >> - tree arg = gimple_call_arg (stmt, i); >> - res_op->ops[i] = do_valueize (arg, top_valueize, valueized); >> - } >> - if (internal_fn_p (cfn) >> - && try_conditional_simplification (as_internal_fn (cfn), >> - res_op, seq, valueize)) >> - return true; >> - switch (num_args) >> - { >> - case 1: >> - return (gimple_resimplify1 (seq, res_op, valueize) >> - || valueized); >> - case 2: >> - return (gimple_resimplify2 (seq, res_op, valueize) >> - || valueized); >> - case 3: >> - return (gimple_resimplify3 (seq, res_op, valueize) >> - || valueized); >> - case 4: >> - return (gimple_resimplify4 (seq, res_op, valueize) >> - || valueized); >> - case 5: >> - return (gimple_resimplify5 (seq, res_op, valueize) >> - || valueized); >> - default: >> - gcc_unreachable (); >> - } >> + res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i)); >> + return -2; >> } >> break; >> >> case GIMPLE_COND: >> { >> - tree lhs = gimple_cond_lhs (stmt); >> - tree rhs = gimple_cond_rhs (stmt); >> - bool valueized = false; >> - lhs = do_valueize (lhs, top_valueize, valueized); >> - rhs = do_valueize (rhs, top_valueize, valueized); >> + tree lhs = valueize_op (gimple_cond_lhs (stmt)); >> + tree rhs = valueize_op (gimple_cond_rhs (stmt)); >> res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, >> rhs); >> - return (gimple_resimplify2 (seq, res_op, valueize) >> - || valueized); >> + return 2; >> } >> >> default: >> break; >> } >> >> - return false; >> + return -1; >> } >> >> +/* Try to describe STMT in RES_OP, returning true on success. >> + For GIMPLE_CONDs, describe the condition that is being tested. >> + For GIMPLE_ASSIGNs, describe the rhs of the assignment. >> + For GIMPLE_CALLs, describe the call. */ >> + >> +bool >> +gimple_extract_op (gimple *stmt, gimple_match_op *res_op) >> +{ >> + auto nop = [](tree op) { return op; }; >> + return gimple_extract (stmt, res_op, nop, nop, nop) != -1; >> +} >> + >> +/* The main STMT based simplification entry. It is used by the fold_stmt >> + and the fold_stmt_to_constant APIs. */ >> + >> +bool >> +gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq, >> + tree (*valueize)(tree), tree (*top_valueize)(tree)) >> +{ >> + bool valueized = false; >> + auto valueize_op = [&](tree op) >> + { >> + return do_valueize (op, top_valueize, valueized); >> + }; >> + auto valueize_condition = [&](tree op) -> tree >> + { >> + bool cond_valueized = false; >> + tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize, >> + cond_valueized); >> + tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize, >> + cond_valueized); >> + gimple_match_op res_op2 (res_op->cond, TREE_CODE (op), >> + TREE_TYPE (op), lhs, rhs); >> + if ((gimple_resimplify2 (seq, &res_op2, valueize) >> + || cond_valueized) >> + && res_op2.code.is_tree_code ()) >> + { >> + if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison) >> + { >> + valueized = true; >> + return build2 (res_op2.code, TREE_TYPE (op), >> + res_op2.ops[0], res_op2.ops[1]); >> + } >> + else if (res_op2.code == SSA_NAME >> + || res_op2.code == INTEGER_CST >> + || res_op2.code == VECTOR_CST) >> + { >> + valueized = true; >> + return res_op2.ops[0]; >> + } >> + } >> + return valueize_op (op); >> + }; >> + auto valueize_name = [&](tree op) >> + { >> + return top_valueize ? top_valueize (op) : op; >> + }; >> + >> + int res = gimple_extract (stmt, res_op, valueize_op, valueize_condition, >> + valueize_name); >> + if (res == -1) >> + return false; >> + >> + if (res == -2) >> + { >> + combined_fn cfn = combined_fn (res_op->code); >> + if (internal_fn_p (cfn) >> + && try_conditional_simplification (as_internal_fn (cfn), >> + res_op, seq, valueize)) >> + return true; >> + res = res_op->num_ops; >> + } >> + >> + switch (res) >> + { >> + case 0: >> + return valueized; >> + case 1: >> + return (gimple_resimplify1 (seq, res_op, valueize) >> + || valueized); >> + case 2: >> + return (gimple_resimplify2 (seq, res_op, valueize) >> + || valueized); >> + case 3: >> + return (gimple_resimplify3 (seq, res_op, valueize) >> + || valueized); >> + case 4: >> + return (gimple_resimplify4 (seq, res_op, valueize) >> + || valueized); >> + case 5: >> + return (gimple_resimplify5 (seq, res_op, valueize) >> + || valueized); > > can't you use just res.resimplify (seq, valueize) || valueized instead of the > switch here when res is not zero? > > Otherwise looks OK. > > Thanks, > Richard. > >> + default: >> + gcc_unreachable (); >> + } >> +} >> >> /* Helper for the autogenerated code, valueize OP. */ >> >> diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h >> index 2d4ea476076..15a0f584db7 100644 >> --- a/gcc/gimple-match.h >> +++ b/gcc/gimple-match.h >> @@ -333,6 +333,7 @@ gimple_simplified_result_is_gimple_val (const >> gimple_match_op *op) >> >> extern tree (*mprts_hook) (gimple_match_op *); >> >> +bool gimple_extract_op (gimple *, gimple_match_op *); >> bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *, >> tree (*)(tree), tree (*)(tree)); >> tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *, >> -- >> 2.25.1 >>