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
>