Re: [PATCH v2 1/3] match: allow c variable for the code/operation of the result [PR126912]
Richard Biener <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CAFiYyc17JWjT+3aKafbQRkCCmkYzRHrj-NMS67CzNMcJ_quwuA@mail.gmail.com> |
On Wed, Aug 19, 2026 at 2:34 AM Andrea Pinski <[email protected]> wrote: > > This allows a C variable for the code of the result > to reduce how many for loops are needed and reduces the code size of > the generated files by a factor of 6 (in some cases). > This should allow for arm to build again at -O0. > > Currently this only allows if the variable contains `code` in it. > If the identifier has cmp in it; genmatch assumes it will be a > tcc_comparison for type resultion. > > I can add support for combined_fn and internal_fn later if needed. > From looking into the match patterns right now; there is a few but none > use a for loop for cases but rather just a few (3/4) if statements. So it > was not high on my list of things to support currently. I do not like the automagic ID recognition too much. Esp. automatically treating anything with 'cmp' as tcc_comparison might lead to wrong code. If we stick with that can you please code-generate runtime checking at least? There might be more problematic cases for type-inference(?) and most definitely for "escape" handling - capture_info::walk_result checks for COND_EXPR and TRUTH_{AND,OR}IF_EXPR but you do not touch this. Runtime checking could be done for those as well. But I wonder whether sth like (simplify (...) (with { tree_code code = ...; } (code<tcc_comparison> ...))) would be better overall, that is, require the set of possible operators to be specified as declared operator list. So it would be a user-id declaration at result parsing time. I thought of (tcc_comparsion code @0 @1) but this doesn't seem lispy syntax. Alternatively do "toplevel" (simplify (...) (with { tree_code = ...; } (with_code code (tcc_comparison) (code @0 @1))) so similar to (for ..) syntax, have a declare syntax. Allow (with_code code (tcc_comparison) code2 (plus minus) as well. I think I prefer (with_code ...)? Richard. > Bootstrapped and tested on x86_64-linux-gnu. > > PR middle-end/126912 > gcc/ChangeLog: > > * doc/match-and-simplify.texi: Document new syntax. > * genmatch.cc (class c_code_id): New class. > (c_code_id::get_c_code_id): New method. > (c_ids): New hashtable. > (test): Handle C_CODE_ID. > (lower_for): Handle c_code_id. > (get_operand_type): Check cmp of c_code_id. > (expr::gen_transform): Likewise. > (dt_simplify::gen_1): Handle C_CODE_ID like CODE. > (parser::parse_operation): Add bool res argument. For unknown id > see if this can be a c code id for res. > (parser::parse_expr): Add bool res argument. Pass down to parse_op > and parse_operation. > (parser::parse_op): Add bool res argument. > Pass down to parse_expr and parse_op. > (parser::parse_result): Pass true to parse_op and parse_expr. > (parser::parse_simplify): Pass false to parse_op. > (main): Allocate and free c_ids. > * match.pd (`(a CMP1 b) bitop (a CMP2 b)`): Remove rcmp > loop and update for the new syntax. > (other comparison loops): Update for the new syntax. > > Signed-off-by: Andrea Pinski <[email protected]> > --- > gcc/doc/match-and-simplify.texi | 13 ++++ > gcc/genmatch.cc | 114 ++++++++++++++++++++++++-------- > gcc/match.pd | 77 +++++++++------------ > 3 files changed, 129 insertions(+), 75 deletions(-) > > diff --git a/gcc/doc/match-and-simplify.texi b/gcc/doc/match-and-simplify.texi > index b187dd27561..4f171a36f69 100644 > --- a/gcc/doc/match-and-simplify.texi > +++ b/gcc/doc/match-and-simplify.texi > @@ -361,6 +361,19 @@ Usually the types of the generated result expressions are > determined from the context, but sometimes like in the above case > it is required that you specify them explicitly. > > +In the result of a simplify, a variable defined in @code{with} can be > +use as the resulting tree code if it contains @code{code} in the name. > +Comparisons results need to have cmp in its name to get the correct type for it. > + > +@smallexample > + (simplify > + (bit_xor (cmp @@0 @@1) integer_truep) > + (with @{ enum tree_code rescmpcode = invert_tree_comparison > + (cmp, HONOR_NANS (@@0)); @} > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (rescmpcode @@0 @@1)))) > +@end smallexample > + > Another modifier for generated expressions is @code{^} which > tells the machinery to try more matches for some special cases. > For example, normally the @code{cond} only allows the gimple > diff --git a/gcc/genmatch.cc b/gcc/genmatch.cc > index 920cf27877d..44da9fa3970 100644 > --- a/gcc/genmatch.cc > +++ b/gcc/genmatch.cc > @@ -1121,7 +1121,7 @@ comparison_code_p (enum tree_code code) > class id_base : public nofree_ptr_hash<id_base> > { > public: > - enum id_kind { CODE, FN, PREDICATE, USER, NULL_ID } kind; > + enum id_kind { CODE, FN, PREDICATE, USER, C_CODE_ID, NULL_ID } kind; > > id_base (id_kind, const char *, int = -1); > > @@ -1199,6 +1199,37 @@ public: > vec<simplify *> matchers; > }; > > +// Identifier that maps to a operator defined by a 'C' identifier. > +// Contains code in the id, also if contains cmp then it is a compare. > + > +class c_code_id : public id_base > +{ > +public: > + c_code_id (const char *id_) > + : id_base (id_base::C_CODE_ID, xstrdup (id_)), > + cmp (strstr (id_, "cmp")) {} > + static id_base *get_c_code_id (const char *); > + ~c_code_id() { delete const_cast<char*>(id); } > + bool cmp; > +}; > + > +static hash_table<id_base> *c_ids; > + > +id_base * > +c_code_id::get_c_code_id (const char *id) > +{ > + if (!strstr (id, "code")) > + return nullptr; > + id_base tem (id_base::C_CODE_ID, id); > + > + id_base **op = c_ids->find_slot_with_hash (&tem, tem.hashval, INSERT); > + if (!*op) > + *op = new c_code_id (id); > + > + return *op; > +} > + > + > /* Identifier that maps to a operator defined by a 'for' directive. */ > > class user_id : public id_base > @@ -1244,6 +1275,14 @@ is_a_helper <user_id *>::test (id_base *id) > return id->kind == id_base::USER; > } > > +template<> > +template<> > +inline bool > +is_a_helper <c_code_id *>::test (id_base *id) > +{ > + return id->kind == id_base::C_CODE_ID; > +} > + > /* If ID has a pair of consecutive, commutative operands, return the > index of the first, otherwise return -1. */ > > @@ -2301,6 +2340,8 @@ lower_for (simplify *sin, vec<simplify *>& simplifiers) > } > else if (is_a <fn_id *> (ids[i]->substitutes[j])) > ; > + else if (is_a <c_code_id *> (ids[i]->substitutes[j])) > + ; > else > can_delay_subst = false; > } > @@ -3222,8 +3263,10 @@ get_operand_type (id_base *op, unsigned pos, > else if (*op == REALPART_EXPR > || *op == IMAGPART_EXPR) > return other_oprnd_type; > - else if (is_a <operator_id *> (op) > - && strcmp (as_a <operator_id *> (op)->tcc, "tcc_comparison") == 0) > + else if ((is_a <operator_id *> (op) > + && strcmp (as_a <operator_id *> (op)->tcc, "tcc_comparison") == 0) > + || (is_a <c_code_id *> (op) > + && as_a <c_code_id *>(op)->cmp)) > return other_oprnd_type; > else if (*op == COND_EXPR > && pos == 0) > @@ -3285,8 +3328,10 @@ expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple, > depth); > type = optype; > } > - else if (is_a <operator_id *> (opr) > - && !strcmp (as_a <operator_id *> (opr)->tcc, "tcc_comparison")) > + else if ((is_a <operator_id *> (opr) > + && !strcmp (as_a <operator_id *> (opr)->tcc, "tcc_comparison")) > + || (is_a <c_code_id *> (opr) > + && as_a <c_code_id *> (opr)->cmp)) > { > /* comparisons use boolean_type_node (or what gets in), but > their operands need to figure out the types themselves. */ > @@ -3451,7 +3496,7 @@ expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple, > fprintf_indent (f, indent + 2, "{\n"); > indent += 4; > } > - if (opr->kind == id_base::CODE) > + if (opr->kind == id_base::CODE || opr->kind == id_base::C_CODE_ID) > fprintf_indent (f, indent, "_r%d = fold_build%d_loc (loc, %s, %s", > depth, ops.length(), opr_name, type); > else > @@ -3460,7 +3505,7 @@ expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple, > for (unsigned i = 0; i < ops.length (); ++i) > fprintf (f, ", _o%d[%u]", depth, i); > fprintf (f, ");\n"); > - if (opr->kind != id_base::CODE) > + if (opr->kind != id_base::CODE && opr->kind != id_base::C_CODE_ID) > { > fprintf_indent (f, indent, "if (!_r%d)\n", depth); > fprintf_indent (f, indent, " goto %s;\n", fail_label); > @@ -4630,7 +4675,7 @@ dt_simplify::gen_1 (FILE *f, int indent, bool gimple, operand *result) > "_r = non_lvalue_loc (loc, res_op0);\n"); > else > { > - if (is_a <operator_id *> (opr)) > + if (is_a <operator_id *> (opr) || is_a <c_code_id *> (opr)) > fprintf_indent (f, indent, > "_r = fold_build%d_loc (loc, %s, type", > e->ops.length (), > @@ -4644,7 +4689,7 @@ dt_simplify::gen_1 (FILE *f, int indent, bool gimple, operand *result) > for (unsigned j = 0; j < e->ops.length (); ++j) > fprintf (f, ", res_op%d", j); > fprintf (f, ");\n"); > - if (!is_a <operator_id *> (opr)) > + if (!is_a <operator_id *> (opr) && !is_a<c_code_id *> (opr)) > { > fprintf_indent (f, indent, "if (!_r)\n"); > fprintf_indent (f, indent, " goto %s;\n", fail_label); > @@ -5146,11 +5191,11 @@ private: > > unsigned get_internal_capture_id (); > > - id_base *parse_operation (unsigned char &); > + id_base *parse_operation (unsigned char &, bool); > operand *parse_capture (operand *, bool); > - operand *parse_expr (); > + operand *parse_expr (bool = false); > c_expr *parse_c_expr (cpp_ttype); > - operand *parse_op (); > + operand *parse_op (bool); > > void record_operlist (location_t, user_id *); > > @@ -5338,7 +5383,7 @@ parser::record_operlist (location_t loc, user_id *p) > convert2? */ > > id_base * > -parser::parse_operation (unsigned char &opt_grp) > +parser::parse_operation (unsigned char &opt_grp, bool res) > { > const cpp_token *id_tok = peek (); > char *alt_id = NULL; > @@ -5365,7 +5410,18 @@ parser::parse_operation (unsigned char &opt_grp) > } > id_base *op = get_operator (alt_id ? alt_id : id); > if (!op) > - fatal_at (id_tok, "unknown operator %s", alt_id ? alt_id : id); > + { > + // An unknown id that contains code in it is considered a C code identifier > + if (res) > + { > + if (id_base *op = c_code_id::get_c_code_id (id)) > + { > + free (alt_id); > + return op; > + } > + } > + fatal_at (id_tok, "unknown operator %s", alt_id ? alt_id : id); > + } > if (alt_id) > free (alt_id); > user_id *p = dyn_cast<user_id *> (op); > @@ -5420,11 +5476,11 @@ parser::parse_capture (operand *op, bool require_existing) > expr = '(' <operation>[capture][flag][type] <operand>... ')' */ > > class operand * > -parser::parse_expr () > +parser::parse_expr (bool res) > { > const cpp_token *token = peek (); > unsigned char opt_grp; > - expr *e = new expr (parse_operation (opt_grp), token->src_loc); > + expr *e = new expr (parse_operation (opt_grp, res), token->src_loc); > token = peek (); > operand *op; > bool is_commutative = false; > @@ -5537,7 +5593,7 @@ parser::parse_expr () > else if (!(token->flags & PREV_WHITE)) > fatal_at (token, "expected expression operand"); > > - e->append_op (parse_op ()); > + e->append_op (parse_op (res)); > } > while (1); > } > @@ -5602,14 +5658,14 @@ parser::parse_c_expr (cpp_ttype start) > op = predicate | expr | c_expr | capture */ > > class operand * > -parser::parse_op () > +parser::parse_op (bool res) > { > const cpp_token *token = peek (); > class operand *op = NULL; > if (token->type == CPP_OPEN_PAREN) > { > eat_token (CPP_OPEN_PAREN); > - op = parse_expr (); > + op = parse_expr (res); > eat_token (CPP_CLOSE_PAREN); > } > else if (token->type == CPP_OPEN_BRACE) > @@ -5695,7 +5751,7 @@ parser::parse_result (operand *result, predicate_id *matcher) > { > const cpp_token *token = peek (); > if (token->type != CPP_OPEN_PAREN) > - return parse_op (); > + return parse_op (true); > > eat_token (CPP_OPEN_PAREN); > if (peek_ident ("if")) > @@ -5709,15 +5765,15 @@ parser::parse_result (operand *result, predicate_id *matcher) > if (peek ()->type == CPP_OPEN_PAREN) > ife->falseexpr = parse_result (result, matcher); > else if (peek ()->type != CPP_CLOSE_PAREN) > - ife->falseexpr = parse_op (); > + ife->falseexpr = parse_op (true); > } > else if (peek ()->type != CPP_CLOSE_PAREN) > { > - ife->trueexpr = parse_op (); > + ife->trueexpr = parse_op (true); > if (peek ()->type == CPP_OPEN_PAREN) > ife->falseexpr = parse_result (result, matcher); > else if (peek ()->type != CPP_CLOSE_PAREN) > - ife->falseexpr = parse_op (); > + ife->falseexpr = parse_op (true); > } > /* If this if is immediately closed then it contains a > manual matcher or is part of a predicate definition. */ > @@ -5752,7 +5808,7 @@ parser::parse_result (operand *result, predicate_id *matcher) > if (peek ()->type == CPP_OPEN_PAREN) > ife->trueexpr = parse_result (result, matcher); > else > - ife->trueexpr = parse_op (); > + ife->trueexpr = parse_op (true); > eat_token (CPP_CLOSE_PAREN); > if (peek ()->type != CPP_OPEN_PAREN > || !peek_ident ("if", 2)) > @@ -5771,7 +5827,7 @@ parser::parse_result (operand *result, predicate_id *matcher) > if (peek ()->type == CPP_OPEN_PAREN) > ife->trueexpr = parse_result (result, matcher); > else > - ife->trueexpr = parse_op (); > + ife->trueexpr = parse_op (true); > if (peek ()->type == CPP_OPEN_PAREN) > fatal_at (peek(), "if inside switch cannot have an else"); > eat_token (CPP_CLOSE_PAREN); > @@ -5787,7 +5843,7 @@ parser::parse_result (operand *result, predicate_id *matcher) > else > { > /* switch default clause */ > - ife->falseexpr = parse_op (); > + ife->falseexpr = parse_op (true); > eat_token (CPP_CLOSE_PAREN); > return res; > } > @@ -5799,7 +5855,7 @@ parser::parse_result (operand *result, predicate_id *matcher) > { > operand *op = result; > if (!matcher) > - op = parse_expr (); > + op = parse_expr (true); > eat_token (CPP_CLOSE_PAREN); > return op; > } > @@ -5826,7 +5882,7 @@ parser::parse_simplify (simplify::simplify_kind kind, > > const cpp_token *loc = peek (); > parsing_match_operand = true; > - class operand *match = parse_op (); > + class operand *match = parse_op (false); > finish_match_operand (match); > parsing_match_operand = false; > if (match->type == operand::OP_CAPTURE && !matcher) > @@ -6366,6 +6422,7 @@ main (int argc, char **argv) > > /* Pre-seed operators. */ > operators = new hash_table<id_base> (1024); > + c_ids = new hash_table<id_base> (4); > #define DEFTREECODE(SYM, STRING, TYPE, NARGS) \ > add_operator (SYM, # SYM, # TYPE, NARGS); > #define END_OF_BASE_TREE_CODES > @@ -6471,6 +6528,7 @@ main (int argc, char **argv) > cpp_destroy (r); > > delete operators; > + delete c_ids; > > return 0; > } > diff --git a/gcc/match.pd b/gcc/match.pd > index eae8717bcfe..d255795c8dd 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -4006,20 +4006,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > (for bitop (bit_and bit_ior) > (for cmp1 (tcc_comparison) > (for cmp2 (tcc_comparison) > - (for rcmp (tcc_comparison) > - (simplify > - (bitop (cmp1 @0 @1) (cmp2 @0 @1)) > - (with { > - tree_code rescode; > - tree res; > - bool honor_nans = HONOR_NANS (@0); > - rescode = combine_comparisons (bitop, cmp1, cmp2, > - type, honor_nans, &res); > - } > - (if (rescode == INTEGER_CST) > - { res; } > - (if (rescode == rcmp) > - (rcmp @0 @1))))))))) > + (simplify > + (bitop (cmp1 @0 @1) (cmp2 @0 @1)) > + (with { > + tree_code rescmpcode; > + tree res; > + bool honor_nans = HONOR_NANS (@0); > + rescmpcode = combine_comparisons (bitop, cmp1, cmp2, > + type, honor_nans, &res); > + } > + (if (rescmpcode == INTEGER_CST) > + { res; } > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (rescmpcode @0 @1)))))))) > > /* (type)([0,1]@a != 0) -> (type)a > (type)([0,1]@a == 1) -> (type)a > @@ -7781,8 +7780,6 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > operator using invert_tree_comparison we have to simulate > that with expression code iteration. */ > (for cmp (tcc_comparison) > - icmp (inverted_tcc_comparison) > - ncmp (inverted_tcc_comparison_with_nans) > /* Ideally we'd like to combine the following two patterns > and handle some more cases by using > (logical_inverted_value (cmp @0 @1)) > @@ -7793,39 +7790,29 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > (if (VECTOR_TYPE_P (type) > || (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) == 1)) > /* Comparison inversion may be impossible for trapping math, > - invert_tree_comparison will tell us. But we can't use > - a computed operator in the replacement tree thus we have > - to play the trick below. */ > - (with { enum tree_code ic = invert_tree_comparison > + invert_tree_comparison will tell us. */ > + (with { enum tree_code rescmpcode = invert_tree_comparison > (cmp, HONOR_NANS (@0)); } > - (if (ic == icmp) > - (icmp @0 @1) > - (if (ic == ncmp) > - (ncmp @0 @1)))))) > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (rescmpcode @0 @1))))) > (simplify > (bit_xor (cmp @0 @1) integer_truep) > - (with { enum tree_code ic = invert_tree_comparison > + (with { enum tree_code rescmpcode = invert_tree_comparison > (cmp, HONOR_NANS (@0)); } > - (if (ic == icmp) > - (icmp @0 @1) > - (if (ic == ncmp) > - (ncmp @0 @1))))) > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (rescmpcode @0 @1)))) > /* ((cast)cmp) - 1 -> -(cast)icmp . */ > (simplify > (plus (convert? (cmp@2 @0 @1)) integer_minus_onep) > (if (TYPE_PRECISION (type) > 1 > && INTEGRAL_TYPE_P (TREE_TYPE (@2)) && TYPE_PRECISION (TREE_TYPE (@2)) == 1) > /* Comparison inversion may be impossible for trapping math, > - invert_tree_comparison will tell us. But we can't use > - a computed operator in the replacement tree thus we have > - to play the trick below. */ > - (with { enum tree_code ic = invert_tree_comparison > + invert_tree_comparison will tell us. */ > + (with { enum tree_code rescmpcode = invert_tree_comparison > (cmp, HONOR_NANS (@0)); > tree cmptype = TREE_TYPE (@2); } > - (if (ic == icmp) > - (negate (convert (icmp:cmptype @0 @1))) > - (if (ic == ncmp) > - (negate (convert (ncmp:cmptype @0 @1)))))))) > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (negate (convert (rescmpcode:cmptype @0 @1))))))) > /* The following bits are handled by fold_binary_op_with_conditional_arg. */ > (simplify > (ne (cmp@2 @0 @1) integer_zerop) > @@ -7838,21 +7825,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > (simplify > (ne (cmp@2 @0 @1) integer_truep) > (if (types_match (type, TREE_TYPE (@2))) > - (with { enum tree_code ic = invert_tree_comparison > + (with { enum tree_code rescmpcode = invert_tree_comparison > (cmp, HONOR_NANS (@0)); } > - (if (ic == icmp) > - (icmp @0 @1) > - (if (ic == ncmp) > - (ncmp @0 @1)))))) > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (rescmpcode @0 @1))))) > (simplify > (eq (cmp@2 @0 @1) integer_zerop) > (if (types_match (type, TREE_TYPE (@2))) > - (with { enum tree_code ic = invert_tree_comparison > + (with { enum tree_code rescmpcode = invert_tree_comparison > (cmp, HONOR_NANS (@0)); } > - (if (ic == icmp) > - (icmp @0 @1) > - (if (ic == ncmp) > - (ncmp @0 @1))))))) > + (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison) > + (rescmpcode @0 @1)))))) > > /* Transform comparisons of the form X - Y CMP 0 to X CMP Y. > ??? The transformation is valid for the other operators if overflow > -- > 2.43.0 >