https://gcc.gnu.org/g:a6c2248cfd4bc922378f554578ee44e6b4690f5d
commit r15-9124-ga6c2248cfd4bc922378f554578ee44e6b4690f5d Author: Jakub Jelinek <ja...@redhat.com> Date: Tue Apr 1 11:40:58 2025 +0200 gimple-low: Diagnose assume attr expressions defining labels which are used as unary && operands outside of those [PR119537] The following testcases ICE on invalid code which defines labels inside of statement expressions and then uses &&label from code outside of the statement expressions. The C++ FE diagnoses that with a warning (not specifically for assume attribute, genericallly about taking address of a label outside of a statement expression so computed goto could violate the requirement that statement expression is not entered from outside of it through a jump into it), the C FE doesn't diagnose anything. Normal direct gotos to such labels are diagnosed by both C and C++. In the assume attribute case it is actually worse than for addresses of labels in normal statement expressions, in that case the labels are still in the current function, so invalid program can still jump to those (and in case of OpenMP/OpenACC where it is also invalid and stuff is moved to a separate function, such movement is done post cfg discovery of FORCED_LABELs and worst case one can run into cases which fail to assemble, but I haven't succeeded in creating ICE for that). For assume at -O0 we'd just throw away the assume expression if it is not a simple condition and so the label is then not defined anywhere and we ICE during cfg pass. The gimplify.cc hunks fix that, as we don't have FORCED_LABELs discovery done yet, it preserves all assume expressions which contain used user labels. With that we ICE during IRA, which is upset about an indirect jump to a label which doesn't exist. So, the gimple-low.cc hunks add diagnostics of the problem, it gathers uids of all the user used labels inside of the assume expressions (usually none) and if it finds any, walks the IL to find uses of those from outside of those expressions now outlined into separate magic functions. 2025-04-01 Jakub Jelinek <ja...@redhat.com> PR middle-end/119537 * gimplify.cc (find_used_user_labels): New function. (gimplify_call_expr): Don't remove complex assume expression at -O0 if it defines any user labels. * gimple-low.cc: Include diagnostic-core.h. (assume_labels): New variable. (diagnose_assume_labels): New function. (lower_function_body): Call it via walk_gimple_seq if assume_labels is non-NULL, then BITMAP_FREE assume_labels. (find_assumption_locals_r): Record in assume_labels uids of user labels defined in assume attribute expressions. * c-c++-common/pr119537-1.c: New test. * c-c++-common/pr119537-2.c: New test. Diff: --- gcc/gimple-low.cc | 43 +++++++++++++++++++++++++++++++++ gcc/gimplify.cc | 25 +++++++++++++++++-- gcc/testsuite/c-c++-common/pr119537-1.c | 23 ++++++++++++++++++ gcc/testsuite/c-c++-common/pr119537-2.c | 23 ++++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/gcc/gimple-low.cc b/gcc/gimple-low.cc index b612970a55ac..79cdd7763dad 100644 --- a/gcc/gimple-low.cc +++ b/gcc/gimple-low.cc @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-inline.h" #include "gimple-walk.h" #include "attribs.h" +#include "diagnostic-core.h" /* The differences between High GIMPLE and Low GIMPLE are the following: @@ -78,6 +79,10 @@ struct lower_data bool cannot_fallthru; }; +/* Bitmap of LABEL_DECL uids for user labels moved into assume outlined + functions. */ +static bitmap assume_labels; + static void lower_stmt (gimple_stmt_iterator *, struct lower_data *); static void lower_gimple_bind (gimple_stmt_iterator *, struct lower_data *); static void lower_try_catch (gimple_stmt_iterator *, struct lower_data *); @@ -87,6 +92,29 @@ static void lower_builtin_posix_memalign (gimple_stmt_iterator *); static void lower_builtin_assume_aligned (gimple_stmt_iterator *); +/* Helper function for lower_function_body, called via walk_gimple_seq. + Diagnose uses of user labels defined inside of assume attribute + expressions. */ + +static tree +diagnose_assume_labels (tree *tp, int *, void *data) +{ + if (TREE_CODE (*tp) == LABEL_DECL + && !DECL_ARTIFICIAL (*tp) + && DECL_NAME (*tp) + && bitmap_bit_p (assume_labels, DECL_UID (*tp))) + { + struct walk_stmt_info *wi = (struct walk_stmt_info *) data; + auto_diagnostic_group d; + error_at (gimple_location (gsi_stmt (wi->gsi)), + "reference to label %qD defined inside of %<assume%> " + "attribute expression from outside of the attribute", *tp); + inform (DECL_SOURCE_LOCATION (*tp), "%qD defined here", *tp); + } + return NULL_TREE; +} + + /* Lower the body of current_function_decl from High GIMPLE into Low GIMPLE. */ @@ -169,6 +197,15 @@ lower_function_body (void) lowered sequence. */ gimple_set_body (current_function_decl, lowered_body); + if (assume_labels) + { + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + walk_gimple_seq (lowered_body, NULL, diagnose_assume_labels, &wi); + BITMAP_FREE (assume_labels); + } + gcc_assert (data.block == DECL_INITIAL (current_function_decl)); BLOCK_SUBBLOCKS (data.block) = blocks_nreverse (BLOCK_SUBBLOCKS (data.block)); @@ -335,6 +372,12 @@ find_assumption_locals_r (gimple_stmt_iterator *gsi_p, bool *, { tree label = gimple_label_label (as_a <glabel *> (stmt)); data->id.decl_map->put (label, label); + if (DECL_NAME (label) && !DECL_ARTIFICIAL (label)) + { + if (assume_labels == NULL) + assume_labels = BITMAP_ALLOC (NULL); + bitmap_set_bit (assume_labels, DECL_UID (label)); + } break; } case GIMPLE_RETURN: diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index 02ad3981adf0..e90220cc2a05 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -4504,6 +4504,21 @@ gimplify_variant_call_expr (tree expr, fallback_t fallback, } +/* Helper function for gimplify_call_expr, called via walk_tree. + Find used user labels. */ + +static tree +find_used_user_labels (tree *tp, int *, void *) +{ + if (TREE_CODE (*tp) == LABEL_EXPR + && !DECL_ARTIFICIAL (LABEL_EXPR_LABEL (*tp)) + && DECL_NAME (LABEL_EXPR_LABEL (*tp)) + && TREE_USED (LABEL_EXPR_LABEL (*tp))) + return *tp; + return NULL_TREE; +} + + /* Gimplify the CALL_EXPR node *EXPR_P into the GIMPLE sequence PRE_P. WANT_VALUE is true if the result of the call is desired. */ @@ -4564,8 +4579,14 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, fallback_t fallback) fndecl, 0)); return GS_OK; } - /* If not optimizing, ignore the assumptions. */ - if (!optimize || seen_error ()) + /* If not optimizing, ignore the assumptions unless there + are used user labels in it. */ + if ((!optimize + && !walk_tree_without_duplicates (&CALL_EXPR_ARG (*expr_p, + 0), + find_used_user_labels, + NULL)) + || seen_error ()) { *expr_p = NULL_TREE; return GS_ALL_DONE; diff --git a/gcc/testsuite/c-c++-common/pr119537-1.c b/gcc/testsuite/c-c++-common/pr119537-1.c new file mode 100644 index 000000000000..7959826d9f76 --- /dev/null +++ b/gcc/testsuite/c-c++-common/pr119537-1.c @@ -0,0 +1,23 @@ +/* PR middle-end/119537 */ +/* { dg-do compile } */ +/* { dg-options "-O0" } */ + +volatile int v; +void *bar (void *, void *); + +void +foo (bool z) +{ + if (z) + goto *&&x; /* { dg-error "reference to label 'x' defined inside of 'assume' attribute expression from outside of the attribute" } */ + /* { dg-message "as a possible target of computed goto" "" { target c++ } .-1 } */ + [[gnu::assume (({ x: v += 1; true; }))]];/* { dg-message "'x' defined here" } */ + /* { dg-warning "jump to label 'x'" "" { target c++ } .-1 } */ + /* { dg-message "enters statement expression" "" { target c++ } .-2 } */ + [[gnu::assume (({ y: v += 1; true; }))]];/* { dg-message "'y' defined here" } */ + /* { dg-warning "jump to label 'y'" "" { target c++ } .-1 } */ + goto *bar (&&x, &&y); /* { dg-error "reference to label 'x' defined inside of 'assume' attribute expression from outside of the attribute" } */ + /* { dg-error "reference to label 'y' defined inside of 'assume' attribute expression from outside of the attribute" "" { target *-*-* } .-1 } */ + /* { dg-message "as a possible target of computed goto" "" { target c++ } .-2 } */ + /* { dg-message "enters statement expression" "" { target c++ } .-3 } */ +} diff --git a/gcc/testsuite/c-c++-common/pr119537-2.c b/gcc/testsuite/c-c++-common/pr119537-2.c new file mode 100644 index 000000000000..7d65672448ac --- /dev/null +++ b/gcc/testsuite/c-c++-common/pr119537-2.c @@ -0,0 +1,23 @@ +/* PR middle-end/119537 */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +volatile int v; +void *bar (void *, void *); + +void +foo (bool z) +{ + if (z) + goto *&&x; /* { dg-error "reference to label 'x' defined inside of 'assume' attribute expression from outside of the attribute" } */ + /* { dg-message "as a possible target of computed goto" "" { target c++ } .-1 } */ + [[gnu::assume (({ x: v += 1; true; }))]];/* { dg-message "'x' defined here" } */ + /* { dg-warning "jump to label 'x'" "" { target c++ } .-1 } */ + /* { dg-message "enters statement expression" "" { target c++ } .-2 } */ + [[gnu::assume (({ y: v += 1; true; }))]];/* { dg-message "'y' defined here" } */ + /* { dg-warning "jump to label 'y'" "" { target c++ } .-1 } */ + goto *bar (&&x, &&y); /* { dg-error "reference to label 'x' defined inside of 'assume' attribute expression from outside of the attribute" } */ + /* { dg-error "reference to label 'y' defined inside of 'assume' attribute expression from outside of the attribute" "" { target *-*-* } .-1 } */ + /* { dg-message "as a possible target of computed goto" "" { target c++ } .-2 } */ + /* { dg-message "enters statement expression" "" { target c++ } .-3 } */ +}