[PATCH 0/2] Make C front end share the C++ tree representation of loops and switches

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 0/2] Make C front end share the C++ tree representation of loops and switches

Sandra Loosemore
This patch series lays some groundwork for the project to redo the
OpenACC "kernels" region support in GCC, described in Thomas
Schwinge's recent talk at the GNU Cauldron:

https://gcc.gnu.org/wiki/cauldron2019talks?action=AttachFile&do=view&target=OpenACC+kernels-cauldron2019.pdf

Briefly, the larger goal is to make the compiler recognize "for" loops
that are candidates for parallelization, and treat them as if they
were annotated with "#pragma acc loop auto".  This is fairly
straightforward to do on the output of the Fortran and C++ front ends,
which both produce a high-level parse tree representation of the loop.
OTOH, the C front end currently lowers loops into a goto form very
early, making it hard both to recognize "for" loops and to
differentiate between (valid) structured and (invalid) unstructured
control flow in the body of the loop.

So, the immediate goal of this patch sequence is to make the C front
end produce output using the same high-level tree representation as
the C++ front end already emits: specifically, to produce FOR_STMT,
WHILE_STMT, and DO_STMT for loops, BREAK_STMT and CONTINUE_STMT
instead of gotos, and also SWITCH_STMT instead of SWITCH_EXPR since
that's mixed up in the handling for "break".  Besides this being
helpful to OpenACC implementation, there's also some argument to be
made that sharing more code between the respective C family front ends
is good from an engineering perspective, etc.

Part 1 of the patch set moves the definitions and support
(genericization, pretty-printers, dumpers, etc) for those tree nodes
out of the cp/ front end and into the common c-family/ code.  Part 2
hacks up the C front end to emit the now-shared data structures.

I bootstrapped and regression-tested this on x86_64-linux-gnu.  There
are a few regressions involving these tests:

gcc.dg/tree-ssa/pr77445-2.c
gcc.dg/tree-ssa/ssa-dce-3.c
gcc.dg/tree-ssa/ssa-dom-thread-7.c

I looked at these briefly and it seems like the problem here is that
the output of the C front end after gimplification is different than
it used to be, since I swapped in the C++ genericization code (it
lowers to a LOOP_EXPR instead of directly to goto form, then the
LOOP_EXPR gets lowered during gimplification).  TBH, I don't really
understand what optimizations these test cases are trying to test, and
whether the pattern-matching is too fragile, whether the form of the code
isn't something that actually tests the optimization, or whether the
optimization is just not working on this input.  So I'm not sure what
to do about those failures!  :-(  I could change the genericizer to
emit the same goto form that the C front end formerly emitted instead
of the C++-style output, but that might just be papering over bugs
elsewhere.  Any advice on how to proceed here is welcome.  If the
patches are OK otherwise, maybe just file bugzilla issues for these
regressions?

-Sandra


Sandra Loosemore (2):
  Move loop and switch tree data structures from cp/ to c-family/.
  Change C front end to emit structured loop and switch tree nodes.

 gcc/c-family/c-common.c             |  24 +++
 gcc/c-family/c-common.def           |  24 +++
 gcc/c-family/c-common.h             |  53 ++++-
 gcc/c-family/c-dump.c               |  38 ++++
 gcc/c-family/c-gimplify.c           | 414 ++++++++++++++++++++++++++++++++++++
 gcc/c-family/c-pretty-print.c       |  92 +++++++-
 gcc/c/c-decl.c                      |  18 +-
 gcc/c/c-lang.h                      |   3 +-
 gcc/c/c-objc-common.h               |   2 +
 gcc/c/c-parser.c                    | 125 ++++++-----
 gcc/c/c-tree.h                      |  21 +-
 gcc/c/c-typeck.c                    | 227 ++++++--------------
 gcc/cp/cp-gimplify.c                | 347 ++----------------------------
 gcc/cp/cp-objcp-common.c            |  13 +-
 gcc/cp/cp-tree.def                  |  23 --
 gcc/cp/cp-tree.h                    |  40 ----
 gcc/cp/cxx-pretty-print.c           |  78 -------
 gcc/cp/dump.c                       |  31 ---
 gcc/doc/generic.texi                |  56 +++--
 gcc/objc/objc-act.c                 |   6 +-
 gcc/testsuite/gcc.dg/gomp/block-7.c |  12 +-
 21 files changed, 859 insertions(+), 788 deletions(-)

--
2.8.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 1/2] Move loop and switch tree data structures from cp/ to c-family/.

Sandra Loosemore
This patch moves the definitions for DO_STMT, FOR_STMT, WHILE_STMT,
SWITCH_STMT, BREAK_STMT, and CONTINUE_STMT from the C++ front end to
c-family.  This includes the genericizers, pretty-printers, and dump
support as well as the tree definitions and accessors.  Some related
code for OMP_FOR and similar OMP constructs is also moved.

2019-11-12  Sandra Loosemore  <[hidden email]>

        gcc/c-family/
        * c-common.c (c_block_may_fallthrough): New, split from
        cxx_block_may_fallthrough in the cp front end.
        (c_common_init_ts): Move handling of loop and switch-related
        statements here from the cp front end.
        * c-common.def (FOR_STMT, WHILE_STMT, DO_STMT): Move here from
        cp front end.
        (BREAK_STMT, CONTINUE_STMT, SWITCH_STMT): Likewise.
        * c-common.h (c_block_may_fallthru): Declare.
        (bc_state_t): Move here from cp front end.
        (save_bc_state, restore_bc_state): Declare.
        (c_genericize_control_stmt): Declare.
        (WHILE_COND, WHILE_BODY): Likewise.
        (DO_COND, DO_BODY): Likewise.
        (FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE): Likewise.
        (SWITCH_STMT_COND, SWITCH_STMT_BODY): Likewise.
        (SWITCH_STMT_TYPE, SWITCH_STMT_SCOPE): Likewise.
        (SWITCH_STMT_ALL_CASES_P, SWITCH_STMT_NO_BREAK_P): Likewise.
        (LABEL_DECL_BREAK, LABEL_DECL_CONTINUE): Likewise.
        * c-dump.c (dump_stmt): Copy from cp front end.
        (c_dump_tree): Move code to handle structured loop and switch
        tree nodes here from cp front end.
        * c-gimplify.c: Adjust includes.
        (enum bc_t, bc_label, begin_bc_block, finish_bc_block): Move from
        cp front end.
        (save_bc_state, restore_bc_state): New functions using old code
        from cp front end.
        (get_bc_label, expr_loc_or_loc): Move from cp front end.
        (genericize_c_loop): Move from cp front end.
        (genericize_for_stmt, genericize_while_stmt): Likewise.
        (genericize_do_stmt, genericize_switch_stmt): Likewise.
        (genericize_continue_stmt, genericize_break_stmt): Likewise.
        (genericize_omp_for_stmt): Likewise.
        (c_genericize_control_stmt): New function using code split from
        cp front end.
        (c_genericize_control_r): New.
        (c_genericize): Call walk_tree with c_genericize_control_r.
        * c-pretty-print.c (c_pretty_printer::statement): Move code to handle
        structured loop and switch tree nodes here from cp front end.

        gcc/cp/
        * cp-gimplify.c (enum bc_t, bc_label): Move to c-family.
        (begin_bc_block, finish_bc_block, get_bc_label): Likewise.
        (genericize_cp_loop): Likewise.
        (genericize_for_stmt, genericize_while_stmt): Likewise.
        (genericize_do_stmt, genericize_switch_stmt): Likewise.
        (genericize_continue_stmt, genericize_break_stmt): Likewise.
        (genericize_omp_for_stmt): Likewise.
        (cp_genericize_r): Call c_genericize_control_stmt instead of
        above functions directly.
        (cp_genericize): Call save_bc_state and restore_bc_state instead
        of manipulating bc_label directly.
        * cp-objcp-common.c (cxx_block_may_fallthru): Defer to
        c_block_may_fallthru instead of handling SWITCH_STMT here.
        (cp_common_init_ts): Move handling of loop and switch-related
        statements to c-family.
        * cp-tree.def (FOR_STMT, WHILE_STMT, DO_STMT): Move to c-family.
        (BREAK_STMT, CONTINUE_STMT, SWITCH_STMT): Likewise.
        * cp-tree.h (LABEL_DECL_BREAK, LABEL_DECL_CONTINUE): Likewise.
        (WHILE_COND, WHILE_BODY): Likewise.
        (DO_COND, DO_BODY): Likewise.
        (FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE): Likewise.
        (SWITCH_STMT_COND, SWITCH_STMT_BODY): Likewise.
        (SWITCH_STMT_TYPE, SWITCH_STMT_SCOPE): Likewise.
        (SWITCH_STMT_ALL_CASES_P, SWITCH_STMT_NO_BREAK_P): Likewise.
        * cxx-pretty-print.c (cxx_pretty_printer::statement): Move code
        to handle structured loop and switch tree nodes to c-family.
        * dump.c (cp_dump_tree): Likewise.

        gcc/
        * doc/generic.texi (Basic Statements): Document SWITCH_EXPR here,
        not SWITCH_STMT.
        (Statements for C and C++): Rename node to reflect what
        the introduction already says about sharing between C and C++
        front ends.  Copy-edit and correct documentation for structured
        loops and switch.
---
 gcc/c-family/c-common.c       |  24 +++
 gcc/c-family/c-common.def     |  24 +++
 gcc/c-family/c-common.h       |  53 +++++-
 gcc/c-family/c-dump.c         |  38 ++++
 gcc/c-family/c-gimplify.c     | 414 ++++++++++++++++++++++++++++++++++++++++++
 gcc/c-family/c-pretty-print.c |  92 +++++++++-
 gcc/cp/cp-gimplify.c          | 347 +++--------------------------------
 gcc/cp/cp-objcp-common.c      |  13 +-
 gcc/cp/cp-tree.def            |  23 ---
 gcc/cp/cp-tree.h              |  40 ----
 gcc/cp/cxx-pretty-print.c     |  78 --------
 gcc/cp/dump.c                 |  31 ----
 gcc/doc/generic.texi          |  56 +++---
 13 files changed, 695 insertions(+), 538 deletions(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 4881199..d83a3be 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5021,6 +5021,24 @@ c_switch_covers_all_cases_p (splay_tree cases, tree type)
   return true;
 }
 
+/* Return true if stmt can fall through.  Used by block_may_fallthru
+   default case.  */
+
+bool
+c_block_may_fallthru (const_tree stmt)
+{
+  switch (TREE_CODE (stmt))
+    {
+    case SWITCH_STMT:
+      return (!SWITCH_STMT_ALL_CASES_P (stmt)
+      || !SWITCH_STMT_NO_BREAK_P (stmt)
+      || block_may_fallthru (SWITCH_STMT_BODY (stmt)));
+
+    default:
+      return true;
+    }
+}
+
 /* Finish an expression taking the address of LABEL (an
    IDENTIFIER_NODE).  Returns an expression for the address.
 
@@ -8002,6 +8020,12 @@ c_common_init_ts (void)
   MARK_TS_EXP (SIZEOF_EXPR);
   MARK_TS_EXP (C_MAYBE_CONST_EXPR);
   MARK_TS_EXP (EXCESS_PRECISION_EXPR);
+  MARK_TS_EXP (BREAK_STMT);
+  MARK_TS_EXP (CONTINUE_STMT);
+  MARK_TS_EXP (DO_STMT);
+  MARK_TS_EXP (FOR_STMT);
+  MARK_TS_EXP (SWITCH_STMT);
+  MARK_TS_EXP (WHILE_STMT);
 }
 
 /* Build a user-defined numeric literal out of an integer constant type VALUE
diff --git a/gcc/c-family/c-common.def b/gcc/c-family/c-common.def
index e24cc69..9f4eb26 100644
--- a/gcc/c-family/c-common.def
+++ b/gcc/c-family/c-common.def
@@ -55,6 +55,30 @@ DEFTREECODE (USERDEF_LITERAL, "userdef_literal", tcc_exceptional, 3)
    or for the purpose of -Wsizeof-pointer-memaccess warning.  */
 DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", tcc_expression, 1)
 
+/* Used to represent a `for' statement. The operands are
+   FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, and FOR_SCOPE,
+   respectively.  */
+DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5)
+
+/* Used to represent a 'while' statement. The operands are WHILE_COND
+   and WHILE_BODY, respectively.  */
+DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 2)
+
+/* Used to represent a 'do' statement. The operands are DO_COND and
+   DO_BODY, respectively.  */
+DEFTREECODE (DO_STMT, "do_stmt", tcc_statement, 2)
+
+/* Used to represent a 'break' statement.  */
+DEFTREECODE (BREAK_STMT, "break_stmt", tcc_statement, 0)
+
+/* Used to represent a 'continue' statement.  */
+DEFTREECODE (CONTINUE_STMT, "continue_stmt", tcc_statement, 0)
+
+/* Used to represent a 'switch' statement. The operands are
+   SWITCH_STMT_COND, SWITCH_STMT_BODY, SWITCH_STMT_TYPE, and
+   SWITCH_STMT_SCOPE, respectively.  */
+DEFTREECODE (SWITCH_STMT, "switch_stmt", tcc_statement, 4)
+
 /*
 Local variables:
 mode:c
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 80a8c9f..0807be5 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1002,6 +1002,7 @@ extern int case_compare (splay_tree_key, splay_tree_key);
 
 extern tree c_add_case_label (location_t, splay_tree, tree, tree, tree);
 extern bool c_switch_covers_all_cases_p (splay_tree, tree);
+extern bool c_block_may_fallthru (const_tree);
 
 extern tree build_function_call (location_t, tree, tree);
 
@@ -1109,7 +1110,15 @@ class substring_loc;
 extern const char *c_get_substring_location (const substring_loc &substr_loc,
      location_t *out_loc);
 
-/* In c-gimplify.c  */
+/* In c-gimplify.c.  */
+typedef struct bc_state
+{
+  tree bc_label[2];
+} bc_state_t;
+extern void save_bc_state (bc_state_t *);
+extern void restore_bc_state (bc_state_t *);
+extern tree c_genericize_control_stmt (tree *, int *, void *,
+       walk_tree_fn, walk_tree_lh);
 extern void c_genericize (tree);
 extern int c_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
 extern tree c_build_bind_expr (location_t, tree, tree);
@@ -1271,6 +1280,48 @@ extern tree build_userdef_literal (tree suffix_id, tree value,
    enum overflow_type overflow,
    tree num_string);
 
+
+/* WHILE_STMT accessors. These give access to the condition of the
+   while statement and the body of the while statement, respectively.  */
+#define WHILE_COND(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0)
+#define WHILE_BODY(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1)
+
+/* DO_STMT accessors. These give access to the condition of the do
+   statement and the body of the do statement, respectively.  */
+#define DO_COND(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 0)
+#define DO_BODY(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 1)
+
+/* FOR_STMT accessors. These give access to the init statement,
+   condition, update expression, and body of the for statement,
+   respectively.  */
+#define FOR_INIT_STMT(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 0)
+#define FOR_COND(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 1)
+#define FOR_EXPR(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 2)
+#define FOR_BODY(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 3)
+#define FOR_SCOPE(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 4)
+
+#define SWITCH_STMT_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0)
+#define SWITCH_STMT_BODY(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 1)
+#define SWITCH_STMT_TYPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 2)
+#define SWITCH_STMT_SCOPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 3)
+/* True if there are case labels for all possible values of switch cond, either
+   because there is a default: case label or because the case label ranges cover
+   all values.  */
+#define SWITCH_STMT_ALL_CASES_P(NODE) \
+  TREE_LANG_FLAG_0 (SWITCH_STMT_CHECK (NODE))
+/* True if the body of a switch stmt contains no BREAK_STMTs.  */
+#define SWITCH_STMT_NO_BREAK_P(NODE) \
+  TREE_LANG_FLAG_2 (SWITCH_STMT_CHECK (NODE))
+
+
+/* Nonzero if NODE is the target for genericization of 'break' stmts.  */
+#define LABEL_DECL_BREAK(NODE) \
+  DECL_LANG_FLAG_0 (LABEL_DECL_CHECK (NODE))
+
+/* Nonzero if NODE is the target for genericization of 'continue' stmts.  */
+#define LABEL_DECL_CONTINUE(NODE) \
+  DECL_LANG_FLAG_1 (LABEL_DECL_CHECK (NODE))
+
 extern bool convert_vector_to_array_for_subscript (location_t, tree *, tree);
 
 /* Possibe cases of scalar_to_vector conversion.  */
diff --git a/gcc/c-family/c-dump.c b/gcc/c-family/c-dump.c
index b31ad08..f5f7316 100644
--- a/gcc/c-family/c-dump.c
+++ b/gcc/c-family/c-dump.c
@@ -26,6 +26,13 @@ along with GCC; see the file COPYING3.  If not see
 
 /* Dump any C-specific tree codes and attributes of common codes.  */
 
+static void
+dump_stmt (dump_info_p di, const_tree t)
+{
+  if (EXPR_HAS_LOCATION (t))
+    dump_int (di, "line", EXPR_LINENO (t));
+}
+
 bool
 c_dump_tree (void *dump_info, tree t)
 {
@@ -42,6 +49,37 @@ c_dump_tree (void *dump_info, tree t)
  dump_string (di, "bitfield");
       break;
 
+    case BREAK_STMT:
+    case CONTINUE_STMT:
+      dump_stmt (di, t);
+      break;
+
+    case DO_STMT:
+      dump_stmt (di, t);
+      dump_child ("body", DO_BODY (t));
+      dump_child ("cond", DO_COND (t));
+      break;
+
+    case FOR_STMT:
+      dump_stmt (di, t);
+      dump_child ("init", FOR_INIT_STMT (t));
+      dump_child ("cond", FOR_COND (t));
+      dump_child ("expr", FOR_EXPR (t));
+      dump_child ("body", FOR_BODY (t));
+      break;
+
+    case SWITCH_STMT:
+      dump_stmt (di, t);
+      dump_child ("cond", SWITCH_STMT_COND (t));
+      dump_child ("body", SWITCH_STMT_BODY (t));
+      break;
+
+    case WHILE_STMT:
+      dump_stmt (di, t);
+      dump_child ("cond", WHILE_COND (t));
+      dump_child ("body", WHILE_BODY (t));
+      break;
+
     default:
       break;
     }
diff --git a/gcc/c-family/c-gimplify.c b/gcc/c-family/c-gimplify.c
index 5636e87..b1c74f7 100644
--- a/gcc/c-family/c-gimplify.c
+++ b/gcc/c-family/c-gimplify.c
@@ -30,6 +30,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "function.h"
 #include "basic-block.h"
 #include "tree.h"
+#include "tree-iterator.h"
+#include "predict.h"
 #include "gimple.h"
 #include "cgraph.h"
 #include "c-pretty-print.h"
@@ -107,6 +109,405 @@ ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
 
 /* Gimplification of statement trees.  */
 
+/* Local declarations.  */
+
+enum bc_t { bc_break = 0, bc_continue = 1 };
+
+/* Stack of labels which are targets for "break" or "continue",
+   linked through TREE_CHAIN.  */
+static tree bc_label[2];
+
+/* Begin a scope which can be exited by a break or continue statement.  BC
+   indicates which.
+
+   Just creates a label with location LOCATION and pushes it into the current
+   context.  */
+
+static tree
+begin_bc_block (enum bc_t bc, location_t location)
+{
+  tree label = create_artificial_label (location);
+  DECL_CHAIN (label) = bc_label[bc];
+  bc_label[bc] = label;
+  if (bc == bc_break)
+    LABEL_DECL_BREAK (label) = true;
+  else
+    LABEL_DECL_CONTINUE (label) = true;
+  return label;
+}
+
+/* Finish a scope which can be exited by a break or continue statement.
+   LABEL was returned from the most recent call to begin_bc_block.  BLOCK is
+   an expression for the contents of the scope.
+
+   If we saw a break (or continue) in the scope, append a LABEL_EXPR to
+   BLOCK.  Otherwise, just forget the label.  */
+
+static void
+finish_bc_block (tree *block, enum bc_t bc, tree label)
+{
+  gcc_assert (label == bc_label[bc]);
+
+  if (TREE_USED (label))
+    append_to_statement_list (build1 (LABEL_EXPR, void_type_node, label),
+      block);
+
+  bc_label[bc] = DECL_CHAIN (label);
+  DECL_CHAIN (label) = NULL_TREE;
+}
+
+/* Allow saving and restoring break/continue state.  */
+
+void
+save_bc_state (bc_state_t *state)
+{
+  state->bc_label[bc_break] = bc_label[bc_break];
+  state->bc_label[bc_continue] = bc_label[bc_continue];
+  bc_label[bc_break] = NULL_TREE;
+  bc_label[bc_continue] = NULL_TREE;
+}
+
+void
+restore_bc_state (bc_state_t *state)
+{
+  gcc_assert (bc_label[bc_break] == NULL);
+  gcc_assert (bc_label[bc_continue] == NULL);
+  bc_label[bc_break] = state->bc_label[bc_break];
+  bc_label[bc_continue] = state->bc_label[bc_continue];
+}
+
+/* Get the LABEL_EXPR to represent a break or continue statement
+   in the current block scope.  BC indicates which.  */
+
+static tree
+get_bc_label (enum bc_t bc)
+{
+  tree label = bc_label[bc];
+  gcc_assert (label);
+
+  /* Mark the label used for finish_bc_block.  */
+  TREE_USED (label) = 1;
+  return label;
+}
+
+/* Return the location from EXPR, or OR_LOC if the former is unknown.  */
+
+location_t
+expr_loc_or_loc (const_tree expr, location_t or_loc)
+{
+  tree t = CONST_CAST_TREE (expr);
+  location_t loc = UNKNOWN_LOCATION;
+  if (t)
+    loc = EXPR_LOCATION (t);
+  if (loc == UNKNOWN_LOCATION)
+    loc = or_loc;
+  return loc;
+}
+
+/* Build a generic representation of one of the C loop forms.  COND is the
+   loop condition or NULL_TREE.  BODY is the (possibly compound) statement
+   controlled by the loop.  INCR is the increment expression of a for-loop,
+   or NULL_TREE.  COND_IS_FIRST indicates whether the condition is
+   evaluated before the loop body as in while and for loops, or after the
+   loop body as in do-while loops.  */
+
+static void
+genericize_c_loop (tree *stmt_p, location_t start_locus, tree cond, tree body,
+   tree incr, bool cond_is_first, int *walk_subtrees,
+   void *data, walk_tree_fn func, walk_tree_lh lh)
+{
+  tree blab, clab;
+  tree exit = NULL;
+  tree stmt_list = NULL;
+  tree debug_begin = NULL;
+
+  if (EXPR_LOCATION (incr) == UNKNOWN_LOCATION)
+    protected_set_expr_location (incr, start_locus);
+
+  walk_tree_1 (&cond, func, data, NULL, lh);
+  walk_tree_1 (&incr, func, data, NULL, lh);
+
+  blab = begin_bc_block (bc_break, start_locus);
+  clab = begin_bc_block (bc_continue, start_locus);
+
+  walk_tree_1 (&body, func, data, NULL, lh);
+  *walk_subtrees = 0;
+
+  if (MAY_HAVE_DEBUG_MARKER_STMTS
+      && (!cond || !integer_zerop (cond)))
+    {
+      debug_begin = build0 (DEBUG_BEGIN_STMT, void_type_node);
+      SET_EXPR_LOCATION (debug_begin, expr_loc_or_loc (cond, start_locus));
+    }
+
+  if (cond && TREE_CODE (cond) != INTEGER_CST)
+    {
+      /* If COND is constant, don't bother building an exit.  If it's false,
+ we won't build a loop.  If it's true, any exits are in the body.  */
+      location_t cloc = expr_loc_or_loc (cond, start_locus);
+      exit = build1_loc (cloc, GOTO_EXPR, void_type_node,
+ get_bc_label (bc_break));
+      /* FIXME: using fold_build3_loc here causes it to sometimes flip
+ the conditional and arms of the COND_EXPR.  This confuses the
+ code to support -Wimplicit-fallthrough and causes failures
+ in the C testsuite.  Folding optimizations ought to get sorted
+ out after gimplification anyway, so don't do it here for now.  */
+      exit = build3_loc (cloc, COND_EXPR, void_type_node, cond,
+ build_empty_stmt (cloc), exit);
+    }
+
+  if (exit && cond_is_first)
+    {
+      append_to_statement_list (debug_begin, &stmt_list);
+      debug_begin = NULL_TREE;
+      append_to_statement_list (exit, &stmt_list);
+    }
+  append_to_statement_list (body, &stmt_list);
+  finish_bc_block (&stmt_list, bc_continue, clab);
+  if (incr)
+    {
+      if (MAY_HAVE_DEBUG_MARKER_STMTS)
+ {
+  tree d = build0 (DEBUG_BEGIN_STMT, void_type_node);
+  SET_EXPR_LOCATION (d, expr_loc_or_loc (incr, start_locus));
+  append_to_statement_list (d, &stmt_list);
+ }
+      append_to_statement_list (incr, &stmt_list);
+    }
+  append_to_statement_list (debug_begin, &stmt_list);
+  if (exit && !cond_is_first)
+    append_to_statement_list (exit, &stmt_list);
+
+  if (!stmt_list)
+    stmt_list = build_empty_stmt (start_locus);
+
+  tree loop;
+  if (cond && integer_zerop (cond))
+    {
+      if (cond_is_first)
+ loop = fold_build3_loc (start_locus, COND_EXPR,
+ void_type_node, cond, stmt_list,
+ build_empty_stmt (start_locus));
+      else
+ loop = stmt_list;
+    }
+  else
+    {
+      location_t loc = start_locus;
+      if (!cond || integer_nonzerop (cond))
+ loc = EXPR_LOCATION (expr_first (body));
+      if (loc == UNKNOWN_LOCATION)
+ loc = start_locus;
+      loop = build1_loc (loc, LOOP_EXPR, void_type_node, stmt_list);
+    }
+
+  stmt_list = NULL;
+  append_to_statement_list (loop, &stmt_list);
+  finish_bc_block (&stmt_list, bc_break, blab);
+  if (!stmt_list)
+    stmt_list = build_empty_stmt (start_locus);
+
+  *stmt_p = stmt_list;
+}
+
+/* Genericize a FOR_STMT node *STMT_P.  */
+
+static void
+genericize_for_stmt (tree *stmt_p, int *walk_subtrees, void *data,
+     walk_tree_fn func, walk_tree_lh lh)
+{
+  tree stmt = *stmt_p;
+  tree expr = NULL;
+  tree loop;
+  tree init = FOR_INIT_STMT (stmt);
+
+  if (init)
+    {
+      walk_tree_1 (&init, func, data, NULL, lh);
+      append_to_statement_list (init, &expr);
+    }
+
+  genericize_c_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt),
+     FOR_BODY (stmt), FOR_EXPR (stmt), 1, walk_subtrees,
+     data, func, lh);
+  append_to_statement_list (loop, &expr);
+  if (expr == NULL_TREE)
+    expr = loop;
+  *stmt_p = expr;
+}
+
+/* Genericize a WHILE_STMT node *STMT_P.  */
+
+static void
+genericize_while_stmt (tree *stmt_p, int *walk_subtrees, void *data,
+       walk_tree_fn func, walk_tree_lh lh)
+{
+  tree stmt = *stmt_p;
+  genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt),
+     WHILE_BODY (stmt), NULL_TREE, 1, walk_subtrees,
+     data, func, lh);
+}
+
+/* Genericize a DO_STMT node *STMT_P.  */
+
+static void
+genericize_do_stmt (tree *stmt_p, int *walk_subtrees, void *data,
+    walk_tree_fn func, walk_tree_lh lh)
+{
+  tree stmt = *stmt_p;
+  genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt),
+     DO_BODY (stmt), NULL_TREE, 0, walk_subtrees,
+     data, func, lh);
+}
+
+/* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR.  */
+
+static void
+genericize_switch_stmt (tree *stmt_p, int *walk_subtrees, void *data,
+ walk_tree_fn func, walk_tree_lh lh)
+{
+  tree stmt = *stmt_p;
+  tree break_block, body, cond, type;
+  location_t stmt_locus = EXPR_LOCATION (stmt);
+
+  body = SWITCH_STMT_BODY (stmt);
+  if (!body)
+    body = build_empty_stmt (stmt_locus);
+  cond = SWITCH_STMT_COND (stmt);
+  type = SWITCH_STMT_TYPE (stmt);
+
+  walk_tree_1 (&cond, func, data, NULL, lh);
+
+  break_block = begin_bc_block (bc_break, stmt_locus);
+
+  walk_tree_1 (&body, func, data, NULL, lh);
+  walk_tree_1 (&type, func, data, NULL, lh);
+  *walk_subtrees = 0;
+
+  if (TREE_USED (break_block))
+    SWITCH_BREAK_LABEL_P (break_block) = 1;
+  finish_bc_block (&body, bc_break, break_block);
+  *stmt_p = build2_loc (stmt_locus, SWITCH_EXPR, type, cond, body);
+  SWITCH_ALL_CASES_P (*stmt_p) = SWITCH_STMT_ALL_CASES_P (stmt);
+  gcc_checking_assert (!SWITCH_STMT_NO_BREAK_P (stmt)
+       || !TREE_USED (break_block));
+}
+
+/* Genericize a CONTINUE_STMT node *STMT_P.  */
+
+static void
+genericize_continue_stmt (tree *stmt_p)
+{
+  tree stmt_list = NULL;
+  tree pred = build_predict_expr (PRED_CONTINUE, NOT_TAKEN);
+  tree label = get_bc_label (bc_continue);
+  location_t location = EXPR_LOCATION (*stmt_p);
+  tree jump = build1_loc (location, GOTO_EXPR, void_type_node, label);
+  append_to_statement_list_force (pred, &stmt_list);
+  append_to_statement_list (jump, &stmt_list);
+  *stmt_p = stmt_list;
+}
+
+/* Genericize a BREAK_STMT node *STMT_P.  */
+
+static void
+genericize_break_stmt (tree *stmt_p)
+{
+  tree label = get_bc_label (bc_break);
+  location_t location = EXPR_LOCATION (*stmt_p);
+  *stmt_p = build1_loc (location, GOTO_EXPR, void_type_node, label);
+}
+
+/* Genericize a OMP_FOR node *STMT_P.  */
+
+static void
+genericize_omp_for_stmt (tree *stmt_p, int *walk_subtrees, void *data,
+ walk_tree_fn func, walk_tree_lh lh)
+{
+  tree stmt = *stmt_p;
+  location_t locus = EXPR_LOCATION (stmt);
+  tree clab = begin_bc_block (bc_continue, locus);
+
+  walk_tree_1 (&OMP_FOR_BODY (stmt), func, data, NULL, lh);
+  if (TREE_CODE (stmt) != OMP_TASKLOOP)
+    walk_tree_1 (&OMP_FOR_CLAUSES (stmt), func, data, NULL, lh);
+  walk_tree_1 (&OMP_FOR_INIT (stmt), func, data, NULL, lh);
+  walk_tree_1 (&OMP_FOR_COND (stmt), func, data, NULL, lh);
+  walk_tree_1 (&OMP_FOR_INCR (stmt), func, data, NULL, lh);
+  walk_tree_1 (&OMP_FOR_PRE_BODY (stmt), func, data, NULL, lh);
+  *walk_subtrees = 0;
+
+  finish_bc_block (&OMP_FOR_BODY (stmt), bc_continue, clab);
+}
+
+
+/* Lower structured control flow tree nodes, such as loops.  The
+   STMT_P, WALK_SUBTREES, and DATA arguments are as for the walk_tree_fn
+   type.  FUNC and LH are language-specific functions passed to walk_tree_1
+   for node visiting and traversal, respectively; they are used to do
+   subtree processing in a language-dependent way.  */
+
+tree
+c_genericize_control_stmt (tree *stmt_p, int *walk_subtrees, void *data,
+   walk_tree_fn func, walk_tree_lh lh)
+{
+  tree stmt = *stmt_p;
+
+  switch (TREE_CODE (stmt))
+    {
+    case FOR_STMT:
+      genericize_for_stmt (stmt_p, walk_subtrees, data, func, lh);
+      break;
+
+    case WHILE_STMT:
+      genericize_while_stmt (stmt_p, walk_subtrees, data, func, lh);
+      break;
+
+    case DO_STMT:
+      genericize_do_stmt (stmt_p, walk_subtrees, data, func, lh);
+      break;
+
+    case SWITCH_STMT:
+      genericize_switch_stmt (stmt_p, walk_subtrees, data, func, lh);
+      break;
+
+    case CONTINUE_STMT:
+      genericize_continue_stmt (stmt_p);
+      break;
+
+    case BREAK_STMT:
+      genericize_break_stmt (stmt_p);
+      break;
+
+    case OMP_FOR:
+    case OMP_SIMD:
+    case OMP_DISTRIBUTE:
+    case OMP_LOOP:
+    case OMP_TASKLOOP:
+    case OACC_LOOP:
+      genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh);
+      break;
+
+    default:
+      break;
+    }
+
+  return NULL;
+}
+
+
+/* Wrapper for c_genericize_control_stmt to allow it to be used as a walk_tree
+   callback.  This is appropriate for C; C++ calls c_genericize_control_stmt
+   directly.  */
+
+static tree
+c_genericize_control_r (tree *stmt_p, int *walk_subtrees, void *data)
+{
+  c_genericize_control_stmt (stmt_p, walk_subtrees, data,
+     c_genericize_control_r, NULL);
+  return NULL;
+}
+
 /* Convert the tree representation of FNDECL from C frontend trees to
    GENERIC.  */
 
@@ -128,6 +529,19 @@ c_genericize (tree fndecl)
     walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
   do_warn_duplicated_branches_r, NULL);
 
+  /* Genericize loops and other structured control constructs.  The C++
+     front end has already done this in lang-specific code.  */
+  if (!c_dialect_cxx ())
+    {
+      bc_state_t save_state;
+      push_cfun (DECL_STRUCT_FUNCTION (fndecl));
+      save_bc_state (&save_state);
+      walk_tree (&DECL_SAVED_TREE (fndecl), c_genericize_control_r,
+ NULL, NULL);
+      restore_bc_state (&save_state);
+      pop_cfun ();
+    }
+
   /* Dump the C-specific tree IR.  */
   dump_orig = get_dump_info (TDI_original, &local_dump_flags);
   if (dump_orig)
diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
index bc73545..262d835 100644
--- a/gcc/c-family/c-pretty-print.c
+++ b/gcc/c-family/c-pretty-print.c
@@ -2348,15 +2348,97 @@ c_pretty_printer::expression (tree e)
 /* Statements.  */
 
 void
-c_pretty_printer::statement (tree stmt)
+c_pretty_printer::statement (tree t)
 {
-  if (stmt == NULL)
+  if (t == NULL)
     return;
 
-  if (pp_needs_newline (this))
-    pp_newline_and_indent (this, 0);
+  switch (TREE_CODE (t))
+    {
+
+    case SWITCH_STMT:
+      pp_c_ws_string (this, "switch");
+      pp_space (this);
+      pp_c_left_paren (this);
+      expression (SWITCH_STMT_COND (t));
+      pp_c_right_paren (this);
+      pp_indentation (this) += 3;
+      pp_needs_newline (this) = true;
+      statement (SWITCH_STMT_BODY (t));
+      pp_newline_and_indent (this, -3);
+      break;
+
+      /* iteration-statement:
+    while ( expression ) statement
+    do statement while ( expression ) ;
+    for ( expression(opt) ; expression(opt) ; expression(opt) ) statement
+    for ( declaration expression(opt) ; expression(opt) ) statement  */
+    case WHILE_STMT:
+      pp_c_ws_string (this, "while");
+      pp_space (this);
+      pp_c_left_paren (this);
+      expression (WHILE_COND (t));
+      pp_c_right_paren (this);
+      pp_newline_and_indent (this, 3);
+      statement (WHILE_BODY (t));
+      pp_indentation (this) -= 3;
+      pp_needs_newline (this) = true;
+      break;
+
+    case DO_STMT:
+      pp_c_ws_string (this, "do");
+      pp_newline_and_indent (this, 3);
+      statement (DO_BODY (t));
+      pp_newline_and_indent (this, -3);
+      pp_c_ws_string (this, "while");
+      pp_space (this);
+      pp_c_left_paren (this);
+      expression (DO_COND (t));
+      pp_c_right_paren (this);
+      pp_c_semicolon (this);
+      pp_needs_newline (this) = true;
+      break;
 
-  dump_generic_node (this, stmt, pp_indentation (this), TDF_NONE, true);
+    case FOR_STMT:
+      pp_c_ws_string (this, "for");
+      pp_space (this);
+      pp_c_left_paren (this);
+      if (FOR_INIT_STMT (t))
+ statement (FOR_INIT_STMT (t));
+      else
+ pp_c_semicolon (this);
+      pp_needs_newline (this) = false;
+      pp_c_whitespace (this);
+      if (FOR_COND (t))
+ expression (FOR_COND (t));
+      pp_c_semicolon (this);
+      pp_needs_newline (this) = false;
+      pp_c_whitespace (this);
+      if (FOR_EXPR (t))
+ expression (FOR_EXPR (t));
+      pp_c_right_paren (this);
+      pp_newline_and_indent (this, 3);
+      statement (FOR_BODY (t));
+      pp_indentation (this) -= 3;
+      pp_needs_newline (this) = true;
+      break;
+
+      /* jump-statement:
+    goto identifier;
+    continue ;
+    return expression(opt) ;  */
+    case BREAK_STMT:
+    case CONTINUE_STMT:
+      pp_string (this, TREE_CODE (t) == BREAK_STMT ? "break" : "continue");
+      pp_c_semicolon (this);
+      pp_needs_newline (this) = true;
+      break;
+
+    default:
+      if (pp_needs_newline (this))
+ pp_newline_and_indent (this, 0);
+      dump_generic_node (this, t, pp_indentation (this), TDF_NONE, true);
+    }
 }
 
 
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 744707e..6fd1118 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -43,66 +43,6 @@ static tree cp_fold_r (tree *, int *, void *);
 static void cp_genericize_tree (tree*, bool);
 static tree cp_fold (tree);
 
-/* Local declarations.  */
-
-enum bc_t { bc_break = 0, bc_continue = 1 };
-
-/* Stack of labels which are targets for "break" or "continue",
-   linked through TREE_CHAIN.  */
-static tree bc_label[2];
-
-/* Begin a scope which can be exited by a break or continue statement.  BC
-   indicates which.
-
-   Just creates a label with location LOCATION and pushes it into the current
-   context.  */
-
-static tree
-begin_bc_block (enum bc_t bc, location_t location)
-{
-  tree label = create_artificial_label (location);
-  DECL_CHAIN (label) = bc_label[bc];
-  bc_label[bc] = label;
-  if (bc == bc_break)
-    LABEL_DECL_BREAK (label) = true;
-  else
-    LABEL_DECL_CONTINUE (label) = true;
-  return label;
-}
-
-/* Finish a scope which can be exited by a break or continue statement.
-   LABEL was returned from the most recent call to begin_bc_block.  BLOCK is
-   an expression for the contents of the scope.
-
-   If we saw a break (or continue) in the scope, append a LABEL_EXPR to
-   BLOCK.  Otherwise, just forget the label.  */
-
-static void
-finish_bc_block (tree *block, enum bc_t bc, tree label)
-{
-  gcc_assert (label == bc_label[bc]);
-
-  if (TREE_USED (label))
-    append_to_statement_list (build1 (LABEL_EXPR, void_type_node, label),
-      block);
-
-  bc_label[bc] = DECL_CHAIN (label);
-  DECL_CHAIN (label) = NULL_TREE;
-}
-
-/* Get the LABEL_EXPR to represent a break or continue statement
-   in the current block scope.  BC indicates which.  */
-
-static tree
-get_bc_label (enum bc_t bc)
-{
-  tree label = bc_label[bc];
-
-  /* Mark the label used for finish_bc_block.  */
-  TREE_USED (label) = 1;
-  return label;
-}
-
 /* Genericize a TRY_BLOCK.  */
 
 static void
@@ -226,229 +166,6 @@ genericize_if_stmt (tree *stmt_p)
   *stmt_p = stmt;
 }
 
-/* Build a generic representation of one of the C loop forms.  COND is the
-   loop condition or NULL_TREE.  BODY is the (possibly compound) statement
-   controlled by the loop.  INCR is the increment expression of a for-loop,
-   or NULL_TREE.  COND_IS_FIRST indicates whether the condition is
-   evaluated before the loop body as in while and for loops, or after the
-   loop body as in do-while loops.  */
-
-static void
-genericize_cp_loop (tree *stmt_p, location_t start_locus, tree cond, tree body,
-    tree incr, bool cond_is_first, int *walk_subtrees,
-    void *data)
-{
-  tree blab, clab;
-  tree exit = NULL;
-  tree stmt_list = NULL;
-  tree debug_begin = NULL;
-
-  if (EXPR_LOCATION (incr) == UNKNOWN_LOCATION)
-    protected_set_expr_location (incr, start_locus);
-
-  cp_walk_tree (&cond, cp_genericize_r, data, NULL);
-  cp_walk_tree (&incr, cp_genericize_r, data, NULL);
-
-  blab = begin_bc_block (bc_break, start_locus);
-  clab = begin_bc_block (bc_continue, start_locus);
-
-  cp_walk_tree (&body, cp_genericize_r, data, NULL);
-  *walk_subtrees = 0;
-
-  if (MAY_HAVE_DEBUG_MARKER_STMTS
-      && (!cond || !integer_zerop (cond)))
-    {
-      debug_begin = build0 (DEBUG_BEGIN_STMT, void_type_node);
-      SET_EXPR_LOCATION (debug_begin, cp_expr_loc_or_loc (cond, start_locus));
-    }
-
-  if (cond && TREE_CODE (cond) != INTEGER_CST)
-    {
-      /* If COND is constant, don't bother building an exit.  If it's false,
- we won't build a loop.  If it's true, any exits are in the body.  */
-      location_t cloc = cp_expr_loc_or_loc (cond, start_locus);
-      exit = build1_loc (cloc, GOTO_EXPR, void_type_node,
- get_bc_label (bc_break));
-      exit = fold_build3_loc (cloc, COND_EXPR, void_type_node, cond,
-      build_empty_stmt (cloc), exit);
-    }
-
-  if (exit && cond_is_first)
-    {
-      append_to_statement_list (debug_begin, &stmt_list);
-      debug_begin = NULL_TREE;
-      append_to_statement_list (exit, &stmt_list);
-    }
-  append_to_statement_list (body, &stmt_list);
-  finish_bc_block (&stmt_list, bc_continue, clab);
-  if (incr)
-    {
-      if (MAY_HAVE_DEBUG_MARKER_STMTS)
- {
-  tree d = build0 (DEBUG_BEGIN_STMT, void_type_node);
-  SET_EXPR_LOCATION (d, cp_expr_loc_or_loc (incr, start_locus));
-  append_to_statement_list (d, &stmt_list);
- }
-      append_to_statement_list (incr, &stmt_list);
-    }
-  append_to_statement_list (debug_begin, &stmt_list);
-  if (exit && !cond_is_first)
-    append_to_statement_list (exit, &stmt_list);
-
-  if (!stmt_list)
-    stmt_list = build_empty_stmt (start_locus);
-
-  tree loop;
-  if (cond && integer_zerop (cond))
-    {
-      if (cond_is_first)
- loop = fold_build3_loc (start_locus, COND_EXPR,
- void_type_node, cond, stmt_list,
- build_empty_stmt (start_locus));
-      else
- loop = stmt_list;
-    }
-  else
-    {
-      location_t loc = start_locus;
-      if (!cond || integer_nonzerop (cond))
- loc = EXPR_LOCATION (expr_first (body));
-      if (loc == UNKNOWN_LOCATION)
- loc = start_locus;
-      loop = build1_loc (loc, LOOP_EXPR, void_type_node, stmt_list);
-    }
-
-  stmt_list = NULL;
-  append_to_statement_list (loop, &stmt_list);
-  finish_bc_block (&stmt_list, bc_break, blab);
-  if (!stmt_list)
-    stmt_list = build_empty_stmt (start_locus);
-
-  *stmt_p = stmt_list;
-}
-
-/* Genericize a FOR_STMT node *STMT_P.  */
-
-static void
-genericize_for_stmt (tree *stmt_p, int *walk_subtrees, void *data)
-{
-  tree stmt = *stmt_p;
-  tree expr = NULL;
-  tree loop;
-  tree init = FOR_INIT_STMT (stmt);
-
-  if (init)
-    {
-      cp_walk_tree (&init, cp_genericize_r, data, NULL);
-      append_to_statement_list (init, &expr);
-    }
-
-  genericize_cp_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt),
-      FOR_BODY (stmt), FOR_EXPR (stmt), 1, walk_subtrees, data);
-  append_to_statement_list (loop, &expr);
-  if (expr == NULL_TREE)
-    expr = loop;
-  *stmt_p = expr;
-}
-
-/* Genericize a WHILE_STMT node *STMT_P.  */
-
-static void
-genericize_while_stmt (tree *stmt_p, int *walk_subtrees, void *data)
-{
-  tree stmt = *stmt_p;
-  genericize_cp_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt),
-      WHILE_BODY (stmt), NULL_TREE, 1, walk_subtrees, data);
-}
-
-/* Genericize a DO_STMT node *STMT_P.  */
-
-static void
-genericize_do_stmt (tree *stmt_p, int *walk_subtrees, void *data)
-{
-  tree stmt = *stmt_p;
-  genericize_cp_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt),
-      DO_BODY (stmt), NULL_TREE, 0, walk_subtrees, data);
-}
-
-/* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR.  */
-
-static void
-genericize_switch_stmt (tree *stmt_p, int *walk_subtrees, void *data)
-{
-  tree stmt = *stmt_p;
-  tree break_block, body, cond, type;
-  location_t stmt_locus = EXPR_LOCATION (stmt);
-
-  body = SWITCH_STMT_BODY (stmt);
-  if (!body)
-    body = build_empty_stmt (stmt_locus);
-  cond = SWITCH_STMT_COND (stmt);
-  type = SWITCH_STMT_TYPE (stmt);
-
-  cp_walk_tree (&cond, cp_genericize_r, data, NULL);
-
-  break_block = begin_bc_block (bc_break, stmt_locus);
-
-  cp_walk_tree (&body, cp_genericize_r, data, NULL);
-  cp_walk_tree (&type, cp_genericize_r, data, NULL);
-  *walk_subtrees = 0;
-
-  if (TREE_USED (break_block))
-    SWITCH_BREAK_LABEL_P (break_block) = 1;
-  finish_bc_block (&body, bc_break, break_block);
-  *stmt_p = build2_loc (stmt_locus, SWITCH_EXPR, type, cond, body);
-  SWITCH_ALL_CASES_P (*stmt_p) = SWITCH_STMT_ALL_CASES_P (stmt);
-  gcc_checking_assert (!SWITCH_STMT_NO_BREAK_P (stmt)
-       || !TREE_USED (break_block));
-}
-
-/* Genericize a CONTINUE_STMT node *STMT_P.  */
-
-static void
-genericize_continue_stmt (tree *stmt_p)
-{
-  tree stmt_list = NULL;
-  tree pred = build_predict_expr (PRED_CONTINUE, NOT_TAKEN);
-  tree label = get_bc_label (bc_continue);
-  location_t location = EXPR_LOCATION (*stmt_p);
-  tree jump = build1_loc (location, GOTO_EXPR, void_type_node, label);
-  append_to_statement_list_force (pred, &stmt_list);
-  append_to_statement_list (jump, &stmt_list);
-  *stmt_p = stmt_list;
-}
-
-/* Genericize a BREAK_STMT node *STMT_P.  */
-
-static void
-genericize_break_stmt (tree *stmt_p)
-{
-  tree label = get_bc_label (bc_break);
-  location_t location = EXPR_LOCATION (*stmt_p);
-  *stmt_p = build1_loc (location, GOTO_EXPR, void_type_node, label);
-}
-
-/* Genericize a OMP_FOR node *STMT_P.  */
-
-static void
-genericize_omp_for_stmt (tree *stmt_p, int *walk_subtrees, void *data)
-{
-  tree stmt = *stmt_p;
-  location_t locus = EXPR_LOCATION (stmt);
-  tree clab = begin_bc_block (bc_continue, locus);
-
-  cp_walk_tree (&OMP_FOR_BODY (stmt), cp_genericize_r, data, NULL);
-  if (TREE_CODE (stmt) != OMP_TASKLOOP)
-    cp_walk_tree (&OMP_FOR_CLAUSES (stmt), cp_genericize_r, data, NULL);
-  cp_walk_tree (&OMP_FOR_INIT (stmt), cp_genericize_r, data, NULL);
-  cp_walk_tree (&OMP_FOR_COND (stmt), cp_genericize_r, data, NULL);
-  cp_walk_tree (&OMP_FOR_INCR (stmt), cp_genericize_r, data, NULL);
-  cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_genericize_r, data, NULL);
-  *walk_subtrees = 0;
-
-  finish_bc_block (&OMP_FOR_BODY (stmt), bc_continue, clab);
-}
-
 /* Hook into the middle of gimplifying an OMP_FOR node.  */
 
 static enum gimplify_status
@@ -1494,7 +1211,8 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       break;
     }
  if (TREE_CODE (stmt) == OMP_TASKLOOP)
-  genericize_omp_for_stmt (stmt_p, walk_subtrees, data);
+  c_genericize_control_stmt (stmt_p, walk_subtrees, data,
+     cp_genericize_r, cp_walk_subtrees);
  else
   cp_walk_tree (&OMP_BODY (stmt), cp_genericize_r, data, NULL);
  wtd->omp_ctx = omp_ctx.outer;
@@ -1561,42 +1279,10 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       gcc_assert (!CONVERT_EXPR_VBASE_PATH (stmt));
       break;
 
-    case FOR_STMT:
-      genericize_for_stmt (stmt_p, walk_subtrees, data);
-      break;
-
-    case WHILE_STMT:
-      genericize_while_stmt (stmt_p, walk_subtrees, data);
-      break;
-
-    case DO_STMT:
-      genericize_do_stmt (stmt_p, walk_subtrees, data);
-      break;
-
-    case SWITCH_STMT:
-      genericize_switch_stmt (stmt_p, walk_subtrees, data);
-      break;
-
-    case CONTINUE_STMT:
-      genericize_continue_stmt (stmt_p);
-      break;
-
-    case BREAK_STMT:
-      genericize_break_stmt (stmt_p);
-      break;
-
     case SPACESHIP_EXPR:
       *stmt_p = genericize_spaceship (*stmt_p);
       break;
 
-    case OMP_FOR:
-    case OMP_SIMD:
-    case OMP_DISTRIBUTE:
-    case OMP_LOOP:
-    case OACC_LOOP:
-      genericize_omp_for_stmt (stmt_p, walk_subtrees, data);
-      break;
-
     case PTRMEM_CST:
       /* By the time we get here we're handing off to the back end, so we don't
  need or want to preserve PTRMEM_CST anymore.  */
@@ -1679,6 +1365,22 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
  TARGET_EXPR_NO_ELIDE (stmt) = 1;
       break;
 
+    case FOR_STMT:
+    case WHILE_STMT:
+    case DO_STMT:
+    case SWITCH_STMT:
+    case CONTINUE_STMT:
+    case BREAK_STMT:
+    case OMP_FOR:
+    case OMP_SIMD:
+    case OMP_DISTRIBUTE:
+    case OMP_LOOP:
+    case OACC_LOOP:
+      /* These cases are handled by shared code.  */
+      c_genericize_control_stmt (stmt_p, walk_subtrees, data,
+ cp_genericize_r, cp_walk_subtrees);
+      break;
+
     default:
       if (IS_TYPE_OR_DECL_P (stmt))
  *walk_subtrees = 0;
@@ -1844,11 +1546,8 @@ cp_genericize (tree fndecl)
     return;
 
   /* Allow cp_genericize calls to be nested.  */
-  tree save_bc_label[2];
-  save_bc_label[bc_break] = bc_label[bc_break];
-  save_bc_label[bc_continue] = bc_label[bc_continue];
-  bc_label[bc_break] = NULL_TREE;
-  bc_label[bc_continue] = NULL_TREE;
+  bc_state_t save_state;
+  save_bc_state (&save_state);
 
   /* We do want to see every occurrence of the parms, so we can't just use
      walk_tree's hash functionality.  */
@@ -1858,11 +1557,7 @@ cp_genericize (tree fndecl)
 
   /* Do everything else.  */
   c_genericize (fndecl);
-
-  gcc_assert (bc_label[bc_break] == NULL);
-  gcc_assert (bc_label[bc_continue] == NULL);
-  bc_label[bc_break] = save_bc_label[bc_break];
-  bc_label[bc_continue] = save_bc_label[bc_continue];
+  restore_bc_state (&save_state);
 }
 
 /* Build code to apply FN to each member of ARG1 and ARG2.  FN may be
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index b9bc2c6..6832ab2 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -314,13 +314,8 @@ cxx_block_may_fallthru (const_tree stmt)
  return true;
       return block_may_fallthru (ELSE_CLAUSE (stmt));
 
-    case SWITCH_STMT:
-      return (!SWITCH_STMT_ALL_CASES_P (stmt)
-      || !SWITCH_STMT_NO_BREAK_P (stmt)
-      || block_may_fallthru (SWITCH_STMT_BODY (stmt)));
-
     default:
-      return true;
+      return c_block_may_fallthru (stmt);
     }
 }
 
@@ -465,20 +460,14 @@ cp_common_init_ts (void)
   MARK_TS_TYPE_NON_COMMON (TYPE_PACK_EXPANSION);
 
   /* Statements.  */
-  MARK_TS_EXP (BREAK_STMT);
   MARK_TS_EXP (CLEANUP_STMT);
-  MARK_TS_EXP (CONTINUE_STMT);
-  MARK_TS_EXP (DO_STMT);
   MARK_TS_EXP (EH_SPEC_BLOCK);
-  MARK_TS_EXP (FOR_STMT);
   MARK_TS_EXP (HANDLER);
   MARK_TS_EXP (IF_STMT);
   MARK_TS_EXP (OMP_DEPOBJ);
   MARK_TS_EXP (RANGE_FOR_STMT);
-  MARK_TS_EXP (SWITCH_STMT);
   MARK_TS_EXP (TRY_BLOCK);
   MARK_TS_EXP (USING_STMT);
-  MARK_TS_EXP (WHILE_STMT);
 
   /* Random expressions.  */
   MARK_TS_EXP (ADDRESSOF_EXPR);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 4e798e3..f04581a 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -298,35 +298,12 @@ DEFTREECODE (CLEANUP_STMT, "cleanup_stmt", tcc_statement, 3)
    and COND_EXPR for the benefit of templates.  */
 DEFTREECODE (IF_STMT, "if_stmt", tcc_statement, 4)
 
-/* Used to represent a `for' statement. The operands are
-   FOR_INIT_STMT, FOR_COND, FOR_EXPR, and FOR_BODY, respectively.  */
-DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5)
-
 /* Used to represent a range-based `for' statement. The operands are
    RANGE_FOR_DECL, RANGE_FOR_EXPR, RANGE_FOR_BODY, RANGE_FOR_SCOPE,
    RANGE_FOR_UNROLL, and RANGE_FOR_INIT_STMT, respectively.  Only used in
    templates.  */
 DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 6)
 
-/* Used to represent a 'while' statement. The operands are WHILE_COND
-   and WHILE_BODY, respectively.  */
-DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 2)
-
-/* Used to represent a 'do' statement. The operands are DO_BODY and
-   DO_COND, respectively.  */
-DEFTREECODE (DO_STMT, "do_stmt", tcc_statement, 2)
-
-/* Used to represent a 'break' statement.  */
-DEFTREECODE (BREAK_STMT, "break_stmt", tcc_statement, 0)
-
-/* Used to represent a 'continue' statement.  */
-DEFTREECODE (CONTINUE_STMT, "continue_stmt", tcc_statement, 0)
-
-/* Used to represent a 'switch' statement. The operands are
-   SWITCH_STMT_COND, SWITCH_STMT_BODY, SWITCH_STMT_TYPE, and
-   SWITCH_STMT_SCOPE, respectively.  */
-DEFTREECODE (SWITCH_STMT, "switch_stmt", tcc_statement, 4)
-
 /* Used to represent an expression statement.  Use `EXPR_STMT_EXPR' to
    obtain the expression.  */
 DEFTREECODE (EXPR_STMT, "expr_stmt", tcc_expression, 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index adc021b..7b42a8b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3993,14 +3993,6 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_LOCAL_FUNCTION_P(NODE) \
   DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE))
 
-/* Nonzero if NODE is the target for genericization of 'break' stmts.  */
-#define LABEL_DECL_BREAK(NODE) \
-  DECL_LANG_FLAG_0 (LABEL_DECL_CHECK (NODE))
-
-/* Nonzero if NODE is the target for genericization of 'continue' stmts.  */
-#define LABEL_DECL_CONTINUE(NODE) \
-  DECL_LANG_FLAG_1 (LABEL_DECL_CHECK (NODE))
-
 /* Nonzero if NODE is the target for genericization of 'return' stmts
    in constructors/destructors of targetm.cxx.cdtor_returns_this targets.  */
 #define LABEL_DECL_CDTOR(NODE) \
@@ -5038,25 +5030,6 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
    building an IF_STMT; IF_STMT_EXTRA_ARGS is used after it is complete.  */
 #define IF_STMT_EXTRA_ARGS(NODE) IF_SCOPE (NODE)
 
-/* WHILE_STMT accessors. These give access to the condition of the
-   while statement and the body of the while statement, respectively.  */
-#define WHILE_COND(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0)
-#define WHILE_BODY(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1)
-
-/* DO_STMT accessors. These give access to the condition of the do
-   statement and the body of the do statement, respectively.  */
-#define DO_COND(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 0)
-#define DO_BODY(NODE) TREE_OPERAND (DO_STMT_CHECK (NODE), 1)
-
-/* FOR_STMT accessors. These give access to the init statement,
-   condition, update expression, and body of the for statement,
-   respectively.  */
-#define FOR_INIT_STMT(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 0)
-#define FOR_COND(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 1)
-#define FOR_EXPR(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 2)
-#define FOR_BODY(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 3)
-#define FOR_SCOPE(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 4)
-
 /* RANGE_FOR_STMT accessors. These give access to the declarator,
    expression, body, and scope of the statement, respectively.  */
 #define RANGE_FOR_DECL(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 0)
@@ -5067,19 +5040,6 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define RANGE_FOR_INIT_STMT(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 5)
 #define RANGE_FOR_IVDEP(NODE) TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE))
 
-#define SWITCH_STMT_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0)
-#define SWITCH_STMT_BODY(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 1)
-#define SWITCH_STMT_TYPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 2)
-#define SWITCH_STMT_SCOPE(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 3)
-/* True if there are case labels for all possible values of switch cond, either
-   because there is a default: case label or because the case label ranges cover
-   all values.  */
-#define SWITCH_STMT_ALL_CASES_P(NODE) \
-  TREE_LANG_FLAG_0 (SWITCH_STMT_CHECK (NODE))
-/* True if the body of a switch stmt contains no BREAK_STMTs.  */
-#define SWITCH_STMT_NO_BREAK_P(NODE) \
-  TREE_LANG_FLAG_2 (SWITCH_STMT_CHECK (NODE))
-
 /* STMT_EXPR accessor.  */
 #define STMT_EXPR_STMT(NODE) TREE_OPERAND (STMT_EXPR_CHECK (NODE), 0)
 
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 8ece11d..4eaf4ed 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -1998,73 +1998,6 @@ cxx_pretty_printer::statement (tree t)
  }
       break;
 
-    case SWITCH_STMT:
-      pp_cxx_ws_string (this, "switch");
-      pp_space (this);
-      pp_cxx_left_paren (this);
-      expression (SWITCH_STMT_COND (t));
-      pp_cxx_right_paren (this);
-      pp_indentation (this) += 3;
-      pp_needs_newline (this) = true;
-      statement (SWITCH_STMT_BODY (t));
-      pp_newline_and_indent (this, -3);
-      break;
-
-      /* iteration-statement:
-    while ( expression ) statement
-    do statement while ( expression ) ;
-    for ( expression(opt) ; expression(opt) ; expression(opt) ) statement
-    for ( declaration expression(opt) ; expression(opt) ) statement  */
-    case WHILE_STMT:
-      pp_cxx_ws_string (this, "while");
-      pp_space (this);
-      pp_cxx_left_paren (this);
-      expression (WHILE_COND (t));
-      pp_cxx_right_paren (this);
-      pp_newline_and_indent (this, 3);
-      statement (WHILE_BODY (t));
-      pp_indentation (this) -= 3;
-      pp_needs_newline (this) = true;
-      break;
-
-    case DO_STMT:
-      pp_cxx_ws_string (this, "do");
-      pp_newline_and_indent (this, 3);
-      statement (DO_BODY (t));
-      pp_newline_and_indent (this, -3);
-      pp_cxx_ws_string (this, "while");
-      pp_space (this);
-      pp_cxx_left_paren (this);
-      expression (DO_COND (t));
-      pp_cxx_right_paren (this);
-      pp_cxx_semicolon (this);
-      pp_needs_newline (this) = true;
-      break;
-
-    case FOR_STMT:
-      pp_cxx_ws_string (this, "for");
-      pp_space (this);
-      pp_cxx_left_paren (this);
-      if (FOR_INIT_STMT (t))
- statement (FOR_INIT_STMT (t));
-      else
- pp_cxx_semicolon (this);
-      pp_needs_newline (this) = false;
-      pp_cxx_whitespace (this);
-      if (FOR_COND (t))
- expression (FOR_COND (t));
-      pp_cxx_semicolon (this);
-      pp_needs_newline (this) = false;
-      pp_cxx_whitespace (this);
-      if (FOR_EXPR (t))
- expression (FOR_EXPR (t));
-      pp_cxx_right_paren (this);
-      pp_newline_and_indent (this, 3);
-      statement (FOR_BODY (t));
-      pp_indentation (this) -= 3;
-      pp_needs_newline (this) = true;
-      break;
-
     case RANGE_FOR_STMT:
       pp_cxx_ws_string (this, "for");
       pp_space (this);
@@ -2088,17 +2021,6 @@ cxx_pretty_printer::statement (tree t)
       pp_needs_newline (this) = true;
       break;
 
-      /* jump-statement:
-    goto identifier;
-    continue ;
-    return expression(opt) ;  */
-    case BREAK_STMT:
-    case CONTINUE_STMT:
-      pp_string (this, TREE_CODE (t) == BREAK_STMT ? "break" : "continue");
-      pp_cxx_semicolon (this);
-      pp_needs_newline (this) = true;
-      break;
-
       /* expression-statement:
     expression(opt) ;  */
     case EXPR_STMT:
diff --git a/gcc/cp/dump.c b/gcc/cp/dump.c
index 744bd73..8e15bd9 100644
--- a/gcc/cp/dump.c
+++ b/gcc/cp/dump.c
@@ -280,25 +280,6 @@ cp_dump_tree (void* dump_info, tree t)
       dump_child ("else", ELSE_CLAUSE (t));
       break;
 
-    case BREAK_STMT:
-    case CONTINUE_STMT:
-      dump_stmt (di, t);
-      break;
-
-    case DO_STMT:
-      dump_stmt (di, t);
-      dump_child ("body", DO_BODY (t));
-      dump_child ("cond", DO_COND (t));
-      break;
-
-    case FOR_STMT:
-      dump_stmt (di, t);
-      dump_child ("init", FOR_INIT_STMT (t));
-      dump_child ("cond", FOR_COND (t));
-      dump_child ("expr", FOR_EXPR (t));
-      dump_child ("body", FOR_BODY (t));
-      break;
-
     case RANGE_FOR_STMT:
       dump_stmt (di, t);
       dump_child ("init", RANGE_FOR_INIT_STMT (t));
@@ -307,18 +288,6 @@ cp_dump_tree (void* dump_info, tree t)
       dump_child ("body", RANGE_FOR_BODY (t));
       break;
 
-    case SWITCH_STMT:
-      dump_stmt (di, t);
-      dump_child ("cond", SWITCH_STMT_COND (t));
-      dump_child ("body", SWITCH_STMT_BODY (t));
-      break;
-
-    case WHILE_STMT:
-      dump_stmt (di, t);
-      dump_child ("cond", WHILE_COND (t));
-      dump_child ("body", WHILE_BODY (t));
-      break;
-
     case STMT_EXPR:
       dump_child ("stmt", STMT_EXPR_STMT (t));
       break;
diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi
index 94e339c..942ef18 100644
--- a/gcc/doc/generic.texi
+++ b/gcc/doc/generic.texi
@@ -2032,14 +2032,19 @@ These nodes represent conditional exits from the nearest enclosing
 nonzero, then the loop should be exited.  An @code{EXIT_EXPR} will only
 appear within a @code{LOOP_EXPR}.
 
-@item SWITCH_STMT
+@item SWITCH_EXPR
 
-Used to represent a @code{switch} statement.  The @code{SWITCH_STMT_COND}
-is the expression on which the switch is occurring.  See the documentation
-for an @code{IF_STMT} for more information on the representation used
-for the condition.  The @code{SWITCH_STMT_BODY} is the body of the switch
-statement.   The @code{SWITCH_STMT_TYPE} is the original type of switch
-expression as given in the source, before any compiler conversions.
+Used to represent a @code{switch} statement.  The @code{SWITCH_COND}
+is the expression on which the switch is occurring.  The
+@code{SWITCH_BODY} is the body of the switch statement.
+@code{SWITCH_ALL_CASES_P} is true if the switch includes a default
+label or the case label ranges cover all possible values of the
+condition expression.
+
+Note that @code{TREE_TYPE} for a @code{SWITCH_EXPR} represents the
+original type of switch expression as given in the source, before any
+compiler conversions, instead of the type of the switch expression
+itself (which is not meaningful).
 
 @item CASE_LABEL_EXPR
 
@@ -2708,7 +2713,7 @@ should submit your patches for inclusion in GCC@.
 * Namespaces::                  Namespaces.
 * Classes::                     Classes.
 * Functions for C++::           Overloading and accessors for C++.
-* Statements for C++::          Statements specific to C and C++.
+* Statements for C and C++::    Statements specific to C and C++.
 * C++ Expressions::    From @code{typeid} to @code{throw}.
 @end menu
 
@@ -3251,8 +3256,8 @@ This predicate holds if the function an overloaded
 @c Function Bodies
 @c ---------------------------------------------------------------------
 
-@node Statements for C++
-@subsection Statements for C++
+@node Statements for C and C++
+@subsection Statements for C and C++
 @cindex statements
 @tindex BREAK_STMT
 @tindex CLEANUP_STMT
@@ -3294,15 +3299,13 @@ This predicate holds if the function an overloaded
 @findex WHILE_BODY
 @findex WHILE_COND
 
-A function that has a definition in the current translation unit will
-have a non-@code{NULL} @code{DECL_INITIAL}.  However, back ends should not make
+A function that has a definition in the current translation unit has
+a non-@code{NULL} @code{DECL_INITIAL}.  However, back ends should not make
 use of the particular value given by @code{DECL_INITIAL}.
 
-The @code{DECL_SAVED_TREE} macro will give the complete body of the
+The @code{DECL_SAVED_TREE} gives the complete body of the
 function.
 
-@subsubsection Statements
-
 There are tree nodes corresponding to all of the source-level
 statement constructs, used within the C and C++ frontends.  These are
 enumerated here, together with a list of the various macros that can
@@ -3327,7 +3330,7 @@ In template functions, the same nodes are used, but sometimes in
 slightly different ways.
 
 Many of the statements have substatements.  For example, a @code{while}
-loop will have a body, which is itself a statement.  If the substatement
+loop has a body, which is itself a statement.  If the substatement
 is @code{NULL_TREE}, it is considered equivalent to a statement
 consisting of a single @code{;}, i.e., an expression statement in which
 the expression has been omitted.  A substatement may in fact be a list
@@ -3356,7 +3359,7 @@ void process_stmt (stmt)
 @end smallexample
 In other words, while the @code{then} clause of an @code{if} statement
 in C++ can be only one statement (although that one statement may be a
-compound statement), the intermediate representation will sometimes use
+compound statement), the intermediate representation sometimes uses
 several statements chained together.
 
 @table @code
@@ -3413,9 +3416,10 @@ the initialization statement for the loop.  The @code{FOR_COND} is the
 termination condition.  The @code{FOR_EXPR} is the expression executed
 right before the @code{FOR_COND} on each loop iteration; often, this
 expression increments a counter.  The body of the loop is given by
-@code{FOR_BODY}.  Note that @code{FOR_INIT_STMT} and @code{FOR_BODY}
-return statements, while @code{FOR_COND} and @code{FOR_EXPR} return
-expressions.
+@code{FOR_BODY}.  @code{FOR_SCOPE} holds the scope of the @code{for}
+statement (used in the C++ front end only).  Note that
+@code{FOR_INIT_STMT} and @code{FOR_BODY} return statements, while
+@code{FOR_COND} and @code{FOR_EXPR} return expressions.
 
 @item HANDLER
 
@@ -3436,8 +3440,6 @@ evaluated, the statement should be executed.  Then, the
 @code{TREE_VALUE} should be used as the conditional expression itself.
 This representation is used to handle C++ code like this:
 
-C++ distinguishes between this and @code{COND_EXPR} for handling templates.
-
 @smallexample
 if (int i = 7) @dots{}
 @end smallexample
@@ -3449,6 +3451,8 @@ The @code{THEN_CLAUSE} represents the statement given by the @code{then}
 condition, while the @code{ELSE_CLAUSE} represents the statement given
 by the @code{else} condition.
 
+C++ distinguishes between this and @code{COND_EXPR} for handling templates.
+
 @item SUBOBJECT
 
 In a constructor, these nodes are used to mark the point at which a
@@ -3465,6 +3469,14 @@ for an @code{IF_STMT} for more information on the representation used
 for the condition.  The @code{SWITCH_STMT_BODY} is the body of the switch
 statement.   The @code{SWITCH_STMT_TYPE} is the original type of switch
 expression as given in the source, before any compiler conversions.
+The @code{SWITCH_STMT_SCOPE} is the statement scope (used in the
+C++ front end only).
+
+There are also two boolean flags used with @code{SWITCH_STMT}.
+@code{SWITCH_STMT_ALL_CASES_P} is true if the switch includes a default label
+or the case label ranges cover all possible values of the condition
+expression.  @code{SWITCH_STMT_NO_BREAK_P} is true if there are no
+@code{break} statements in the switch.
 
 @item TRY_BLOCK
 Used to represent a @code{try} block.  The body of the try block is
--
2.8.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 2/2] Change C front end to emit structured loop and switch tree nodes.

Sandra Loosemore
In reply to this post by Sandra Loosemore
2019-11-12  Sandra Loosemore  <[hidden email]>

        gcc/c
        * c-decl.c (c_break_label, c_cont_label): Delete, and replace
        with...
        (in_statement): New.
        (start_function): Adjust for above change.
        (c_push_function_context, c_pop_function_context): Likewise.
        * c-lang.h (struct language_function): Likewise.
        * c-objc-common.h (LANG_HOOKS_BLOCK_MAY_FALLTHRU): Define.
        * c-parser.c (objc_foreach_break_label, objc_foreach_continue_label):
        New.
        (c_parser_statement_after_labels): Adjust calls to c_finish_bc_stmt.
        (c_parser_switch_statement): Adjust break/switch context handling
        and calls to renamed functions.
        (c_parser_while_statement): Adjust break/switch context handling and
        build a WHILE_STMT.
        (c_parser_do_statement): Ditto, with DO_STMT respectively.
        (c_parser_for_statement): Ditto, with FOR_STMT respectively.
        (c_parser_omp_for_loop): Adjust break/switch context handling.
        * c-tree.h (c_break_label, c_cont_label): Delete.
        (IN_SWITCH_STMT, IN_ITERATION_STMT): Define.
        (IN_OMP_BLOCK, IN_OMP_FOR, IN_OBJC_FOREACH): Define.
        (in_statement, switch_statement_break_seen_p): Declare.
        (c_start_case, c_finish_case): Renamed to...
        (c_start_switch, c_finish_switch).
        (c_finish_bc_stmt): Adjust arguments.
        * c-typeck.c (build_function_call_vec): Don't try to print
        statements with %qE format.
        (struct c_switch):  Rename switch_expr field to switch_stmt.
        Add break_stmt_seen_p field.
        (c_start_case): Rename to c_start_switch.  Build a SWITCH_STMT
        instead of a SWITCH_EXPR.  Update for changes to struct c_switch.
        (do_case): Update for changes to struct c_switch.
        (c_finish_case): Rename to c_finish_switch.  Update for changes to
        struct c_switch and change of representation from SWITCH_EXPR to
        SWITCH_STMT.
        (c_finish_loop): Delete.
        (c_finish_bc_stmt): Update to reflect changes to break/continue
        state representation.  Build a BREAK_STMT or CONTINUE_STMT instead
        of a GOTO_EXPR except for objc foreach loops.

        gcc/objc
        * objc-act.c (objc_start_method_definition): Update to reflect
        changes to break/continue state bookkeeping in C front end.

        gcc/testsuite/
        * gcc.dg/gomp/block-7.c: Update expected error message wording.
---
 gcc/c/c-decl.c                      |  18 ++-
 gcc/c/c-lang.h                      |   3 +-
 gcc/c/c-objc-common.h               |   2 +
 gcc/c/c-parser.c                    | 125 ++++++++++----------
 gcc/c/c-tree.h                      |  21 +++-
 gcc/c/c-typeck.c                    | 227 +++++++++++-------------------------
 gcc/objc/objc-act.c                 |   6 +-
 gcc/testsuite/gcc.dg/gomp/block-7.c |  12 +-
 8 files changed, 164 insertions(+), 250 deletions(-)

diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 2841b4f..e5b01bf 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -112,9 +112,9 @@ struct obstack parser_obstack;
 
 static GTY(()) struct stmt_tree_s c_stmt_tree;
 
-/* State saving variables.  */
-tree c_break_label;
-tree c_cont_label;
+/* Zero if we are not in an iteration or switch statement, otherwise
+   a bitmask.  See bitmask definitions in c-tree.h.  */
+unsigned char in_statement;
 
 /* A list of decls to be made automatically visible in each file scope.  */
 static GTY(()) tree visible_builtins;
@@ -8970,10 +8970,8 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
   warn_about_return_type = 0;
   c_switch_stack = NULL;
 
-  /* Indicate no valid break/continue context by setting these variables
-     to some non-null, non-label value.  We'll notice and emit the proper
-     error message in c_finish_bc_stmt.  */
-  c_break_label = c_cont_label = size_zero_node;
+  /* Indicate no valid break/continue context.  */
+  in_statement = 0;
 
   decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, true, NULL,
   &attributes, NULL, NULL, DEPRECATED_NORMAL);
@@ -9964,8 +9962,7 @@ c_push_function_context (void)
 
   p->base.x_stmt_tree = c_stmt_tree;
   c_stmt_tree.x_cur_stmt_list = vec_safe_copy (c_stmt_tree.x_cur_stmt_list);
-  p->x_break_label = c_break_label;
-  p->x_cont_label = c_cont_label;
+  p->x_in_statement = in_statement;
   p->x_switch_stack = c_switch_stack;
   p->arg_info = current_function_arg_info;
   p->returns_value = current_function_returns_value;
@@ -10004,8 +10001,7 @@ c_pop_function_context (void)
 
   c_stmt_tree = p->base.x_stmt_tree;
   p->base.x_stmt_tree.x_cur_stmt_list = NULL;
-  c_break_label = p->x_break_label;
-  c_cont_label = p->x_cont_label;
+  in_statement = p->x_in_statement;
   c_switch_stack = p->x_switch_stack;
   current_function_arg_info = p->arg_info;
   current_function_returns_value = p->returns_value;
diff --git a/gcc/c/c-lang.h b/gcc/c/c-lang.h
index 3ddeb8f..ed1c985 100644
--- a/gcc/c/c-lang.h
+++ b/gcc/c/c-lang.h
@@ -51,8 +51,7 @@ struct GTY(()) lang_decl {
 
 struct GTY(()) language_function {
   struct c_language_function base;
-  tree x_break_label;
-  tree x_cont_label;
+  unsigned char x_in_statement;
   struct c_switch * GTY((skip)) x_switch_stack;
   struct c_arg_info * GTY((skip)) arg_info;
   int returns_value;
diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h
index c8739e0..269b00c 100644
--- a/gcc/c/c-objc-common.h
+++ b/gcc/c/c-objc-common.h
@@ -56,6 +56,8 @@ along with GCC; see the file COPYING3.  If not see
 #define LANG_HOOKS_TYPES_COMPATIBLE_P c_types_compatible_p
 #undef LANG_HOOKS_MISSING_NORETURN_OK_P
 #define LANG_HOOKS_MISSING_NORETURN_OK_P c_missing_noreturn_ok_p
+#undef LANG_HOOKS_BLOCK_MAY_FALLTHRU
+#define LANG_HOOKS_BLOCK_MAY_FALLTHRU c_block_may_fallthru
 #undef  LANG_HOOKS_BUILTIN_FUNCTION
 #define LANG_HOOKS_BUILTIN_FUNCTION c_builtin_function
 #undef  LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 12deb3e..2173ea9 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1384,6 +1384,9 @@ struct oacc_routine_data {
   location_t loc;
 };
 
+/* Used for parsing objc foreach statements.  */
+static tree objc_foreach_break_label, objc_foreach_continue_label;
+
 static void c_parser_external_declaration (c_parser *);
 static void c_parser_asm_definition (c_parser *);
 static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool,
@@ -5736,11 +5739,11 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p,
   goto expect_semicolon;
  case RID_CONTINUE:
   c_parser_consume_token (parser);
-  stmt = c_finish_bc_stmt (loc, &c_cont_label, false);
+  stmt = c_finish_bc_stmt (loc, objc_foreach_continue_label, false);
   goto expect_semicolon;
  case RID_BREAK:
   c_parser_consume_token (parser);
-  stmt = c_finish_bc_stmt (loc, &c_break_label, true);
+  stmt = c_finish_bc_stmt (loc, objc_foreach_break_label, true);
   goto expect_semicolon;
  case RID_RETURN:
   c_parser_consume_token (parser);
@@ -6142,7 +6145,8 @@ static void
 c_parser_switch_statement (c_parser *parser, bool *if_p)
 {
   struct c_expr ce;
-  tree block, expr, body, save_break;
+  tree block, expr, body;
+  unsigned char save_in_statement;
   location_t switch_loc = c_parser_peek_token (parser)->location;
   location_t switch_cond_loc;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_SWITCH));
@@ -6168,9 +6172,9 @@ c_parser_switch_statement (c_parser *parser, bool *if_p)
       expr = error_mark_node;
       ce.original_type = error_mark_node;
     }
-  c_start_case (switch_loc, switch_cond_loc, expr, explicit_cast_p);
-  save_break = c_break_label;
-  c_break_label = NULL_TREE;
+  c_start_switch (switch_loc, switch_cond_loc, expr, explicit_cast_p);
+  save_in_statement = in_statement;
+  in_statement |= IN_SWITCH_STMT;
   location_t loc_after_labels;
   bool open_brace_p = c_parser_peek_token (parser)->type == CPP_OPEN_BRACE;
   body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels);
@@ -6178,16 +6182,8 @@ c_parser_switch_statement (c_parser *parser, bool *if_p)
   if (!open_brace_p && c_parser_peek_token (parser)->type != CPP_SEMICOLON)
     warn_for_multistatement_macros (loc_after_labels, next_loc, switch_loc,
     RID_SWITCH);
-  if (c_break_label)
-    {
-      location_t here = c_parser_peek_token (parser)->location;
-      tree t = build1 (LABEL_EXPR, void_type_node, c_break_label);
-      SET_EXPR_LOCATION (t, here);
-      SWITCH_BREAK_LABEL_P (c_break_label) = 1;
-      append_to_statement_list_force (t, &body);
-    }
-  c_finish_case (body, ce.original_type);
-  c_break_label = save_break;
+  c_finish_switch (body, ce.original_type);
+  in_statement = save_in_statement;
   add_stmt (c_end_compound_stmt (switch_loc, block, flag_isoc99));
   c_parser_maybe_reclassify_token (parser);
 }
@@ -6205,7 +6201,8 @@ static void
 c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
   bool *if_p)
 {
-  tree block, cond, body, save_break, save_cont;
+  tree block, cond, body;
+  unsigned char save_in_statement;
   location_t loc;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE));
   token_indent_info while_tinfo
@@ -6224,10 +6221,8 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
    build_int_cst (integer_type_node,
   annot_expr_unroll_kind),
    build_int_cst (integer_type_node, unroll));
-  save_break = c_break_label;
-  c_break_label = NULL_TREE;
-  save_cont = c_cont_label;
-  c_cont_label = NULL_TREE;
+  save_in_statement = in_statement;
+  in_statement = IN_ITERATION_STMT;
 
   token_indent_info body_tinfo
     = get_token_indent_info (c_parser_peek_token (parser));
@@ -6235,8 +6230,7 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
   location_t loc_after_labels;
   bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE);
   body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels);
-  c_finish_loop (loc, loc, cond, UNKNOWN_LOCATION, NULL, body,
- c_break_label, c_cont_label, true);
+  add_stmt (build_stmt (loc, WHILE_STMT, cond, body));
   add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
   c_parser_maybe_reclassify_token (parser);
 
@@ -6248,8 +6242,7 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
     warn_for_multistatement_macros (loc_after_labels, next_tinfo.location,
     while_tinfo.location, RID_WHILE);
 
-  c_break_label = save_break;
-  c_cont_label = save_cont;
+  in_statement = save_in_statement;
 }
 
 /* Parse a do statement (C90 6.6.5, C99 6.8.5, C11 6.8.5).
@@ -6261,7 +6254,8 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
 static void
 c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll)
 {
-  tree block, cond, body, save_break, save_cont, new_break, new_cont;
+  tree block, cond, body;
+  unsigned char save_in_statement;
   location_t loc;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO));
   c_parser_consume_token (parser);
@@ -6271,17 +6265,11 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll)
  "suggest braces around empty body in %<do%> statement");
   block = c_begin_compound_stmt (flag_isoc99);
   loc = c_parser_peek_token (parser)->location;
-  save_break = c_break_label;
-  c_break_label = NULL_TREE;
-  save_cont = c_cont_label;
-  c_cont_label = NULL_TREE;
+  save_in_statement = in_statement;
+  in_statement = IN_ITERATION_STMT;
   body = c_parser_c99_block_statement (parser, NULL);
   c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>");
-  new_break = c_break_label;
-  c_break_label = save_break;
-  new_cont = c_cont_label;
-  c_cont_label = save_cont;
-  location_t cond_loc = c_parser_peek_token (parser)->location;
+  in_statement = save_in_statement;
   cond = c_parser_paren_condition (parser);
   if (ivdep && cond != error_mark_node)
     cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
@@ -6295,8 +6283,8 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll)
    build_int_cst (integer_type_node, unroll));
   if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
     c_parser_skip_to_end_of_block_or_statement (parser);
-  c_finish_loop (loc, cond_loc, cond, UNKNOWN_LOCATION, NULL, body,
- new_break, new_cont, false);
+
+  add_stmt (build_stmt (loc, DO_STMT, cond, body));
   add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
 }
 
@@ -6363,15 +6351,15 @@ static void
 c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
  bool *if_p)
 {
-  tree block, cond, incr, save_break, save_cont, body;
+  tree block, cond, incr, body;
+  unsigned char save_in_statement;
+  tree save_objc_foreach_break_label, save_objc_foreach_continue_label;
   /* The following are only used when parsing an ObjC foreach statement.  */
   tree object_expression;
   /* Silence the bogus uninitialized warning.  */
   tree collection_expression = NULL;
   location_t loc = c_parser_peek_token (parser)->location;
   location_t for_loc = loc;
-  location_t cond_loc = UNKNOWN_LOCATION;
-  location_t incr_loc = UNKNOWN_LOCATION;
   bool is_foreach_statement = false;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
   token_indent_info for_tinfo
@@ -6479,7 +6467,6 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
       gcc_assert (!parser->objc_could_be_foreach_context);
       if (!is_foreach_statement)
  {
-  cond_loc = c_parser_peek_token (parser)->location;
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
     {
       if (ivdep)
@@ -6520,7 +6507,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
       /* Parse the increment expression (the third expression in a
  for-statement).  In the case of a foreach-statement, this is
  the expression that follows the 'in'.  */
-      loc = incr_loc = c_parser_peek_token (parser)->location;
+      loc = c_parser_peek_token (parser)->location;
       if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
  {
   if (is_foreach_statement)
@@ -6546,10 +6533,17 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
  }
       parens.skip_until_found_close (parser);
     }
-  save_break = c_break_label;
-  c_break_label = NULL_TREE;
-  save_cont = c_cont_label;
-  c_cont_label = NULL_TREE;
+  save_in_statement = in_statement;
+  if (is_foreach_statement)
+    {
+      in_statement = IN_OBJC_FOREACH;
+      save_objc_foreach_break_label = objc_foreach_break_label;
+      save_objc_foreach_continue_label = objc_foreach_continue_label;
+      objc_foreach_break_label = create_artificial_label (loc);
+      objc_foreach_continue_label = create_artificial_label (loc);
+    }
+  else
+    in_statement = IN_ITERATION_STMT;
 
   token_indent_info body_tinfo
     = get_token_indent_info (c_parser_peek_token (parser));
@@ -6560,11 +6554,12 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
 
   if (is_foreach_statement)
     objc_finish_foreach_loop (for_loc, object_expression,
-      collection_expression, body, c_break_label,
-      c_cont_label);
+      collection_expression, body,
+      objc_foreach_break_label,
+      objc_foreach_continue_label);
   else
-    c_finish_loop (for_loc, cond_loc, cond, incr_loc, incr, body,
-   c_break_label, c_cont_label, true);
+    add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr,
+  body, NULL_TREE));
   add_stmt (c_end_compound_stmt (for_loc, block,
  flag_isoc99 || c_dialect_objc ()));
   c_parser_maybe_reclassify_token (parser);
@@ -6577,8 +6572,12 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
     warn_for_multistatement_macros (loc_after_labels, next_tinfo.location,
     for_tinfo.location, RID_FOR);
 
-  c_break_label = save_break;
-  c_cont_label = save_cont;
+  in_statement = save_in_statement;
+  if (is_foreach_statement)
+    {
+      objc_foreach_break_label = save_objc_foreach_break_label;
+      objc_foreach_continue_label = save_objc_foreach_continue_label;
+    }
 }
 
 /* Parse an asm statement, a GNU extension.  This is a full-blown asm
@@ -17433,7 +17432,8 @@ static tree
 c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
        tree clauses, tree *cclauses, bool *if_p)
 {
-  tree decl, cond, incr, save_break, save_cont, body, init, stmt, cl;
+  tree decl, cond, incr, body, init, stmt, cl;
+  unsigned char save_in_statement;
   tree declv, condv, incrv, initv, ret = NULL_TREE;
   tree pre_body = NULL_TREE, this_pre_body;
   tree ordered_cl = NULL_TREE;
@@ -17501,6 +17501,11 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
   for_loc = c_parser_peek_token (parser)->location;
   c_parser_consume_token (parser);
 
+  /* Forbid break/continue in the loop initializer, condition, and
+     increment expressions.  */
+  save_in_statement = in_statement;
+  in_statement = IN_OMP_BLOCK;
+
   for (i = 0; i < count; i++)
     {
       int bracecount = 0;
@@ -17676,10 +17681,7 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
   if (nbraces)
     if_p = NULL;
 
-  save_break = c_break_label;
-  c_break_label = size_one_node;
-  save_cont = c_cont_label;
-  c_cont_label = NULL_TREE;
+  in_statement = IN_OMP_FOR;
   body = push_stmt_list ();
 
   if (inscan)
@@ -17693,16 +17695,9 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
     }
   else
     add_stmt (c_parser_c99_block_statement (parser, if_p));
-  if (c_cont_label)
-    {
-      tree t = build1 (LABEL_EXPR, void_type_node, c_cont_label);
-      SET_EXPR_LOCATION (t, loc);
-      add_stmt (t);
-    }
 
   body = pop_stmt_list (body);
-  c_break_label = save_break;
-  c_cont_label = save_cont;
+  in_statement = save_in_statement;
 
   while (nbraces)
     {
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 71cd77d..27fabc0 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -527,8 +527,19 @@ extern void gen_aux_info_record (tree, int, int, int);
 struct c_spot_bindings;
 class c_struct_parse_info;
 extern struct obstack parser_obstack;
-extern tree c_break_label;
-extern tree c_cont_label;
+/* Set to IN_ITERATION_STMT if parsing an iteration-statement,
+   to IN_OMP_BLOCK if parsing OpenMP structured block and
+   IN_OMP_FOR if parsing OpenMP loop.  If parsing a switch statement,
+   this is bitwise ORed with IN_SWITCH_STMT, unless parsing an
+   iteration-statement, OpenMP block or loop within that switch.  */
+#define IN_SWITCH_STMT 1
+#define IN_ITERATION_STMT 2
+#define IN_OMP_BLOCK 4
+#define IN_OMP_FOR 8
+#define IN_OBJC_FOREACH 16
+extern unsigned char in_statement;
+
+extern bool switch_statement_break_seen_p;
 
 extern bool global_bindings_p (void);
 extern tree pushdecl (tree);
@@ -690,8 +701,8 @@ extern void process_init_element (location_t, struct c_expr, bool,
 extern tree build_compound_literal (location_t, tree, tree, bool,
     unsigned int);
 extern void check_compound_literal_type (location_t, struct c_type_name *);
-extern tree c_start_case (location_t, location_t, tree, bool);
-extern void c_finish_case (tree, tree);
+extern tree c_start_switch (location_t, location_t, tree, bool);
+extern void c_finish_switch (tree, tree);
 extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool,
     bool);
 extern tree build_asm_stmt (bool, tree);
@@ -706,7 +717,7 @@ extern tree c_finish_stmt_expr (location_t, tree);
 extern tree c_process_expr_stmt (location_t, tree);
 extern tree c_finish_expr_stmt (location_t, tree);
 extern tree c_finish_return (location_t, tree, tree);
-extern tree c_finish_bc_stmt (location_t, tree *, bool);
+extern tree c_finish_bc_stmt (location_t, tree, bool);
 extern tree c_finish_goto_label (location_t, tree);
 extern tree c_finish_goto_ptr (location_t, tree);
 extern tree c_expr_to_decl (tree, bool *, bool *);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 793d10e..540ad19 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3060,7 +3060,7 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
   if (!(TREE_CODE (fntype) == POINTER_TYPE
  && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE))
     {
-      if (!flag_diagnostics_show_caret)
+      if (!flag_diagnostics_show_caret && !STATEMENT_CLASS_P (function))
  error_at (loc,
   "called object %qE is not a function or function pointer",
   function);
@@ -10688,8 +10688,8 @@ c_finish_return (location_t loc, tree retval, tree origtype)
 }
 
 struct c_switch {
-  /* The SWITCH_EXPR being built.  */
-  tree switch_expr;
+  /* The SWITCH_STMT being built.  */
+  tree switch_stmt;
 
   /* The original type of the testing expression, i.e. before the
      default conversion is applied.  */
@@ -10706,6 +10706,9 @@ struct c_switch {
      warnings crossing decls when branching to a case label.  */
   struct c_spot_bindings *bindings;
 
+  /* Whether the switch includes any break statements.  */
+  bool break_stmt_seen_p;
+
   /* The next node on the stack.  */
   struct c_switch *next;
 
@@ -10723,14 +10726,14 @@ struct c_switch {
 struct c_switch *c_switch_stack;
 
 /* Start a C switch statement, testing expression EXP.  Return the new
-   SWITCH_EXPR.  SWITCH_LOC is the location of the `switch'.
+   SWITCH_STMT.  SWITCH_LOC is the location of the `switch'.
    SWITCH_COND_LOC is the location of the switch's condition.
    EXPLICIT_CAST_P is true if the expression EXP has an explicit cast.  */
 
 tree
-c_start_case (location_t switch_loc,
-      location_t switch_cond_loc,
-      tree exp, bool explicit_cast_p)
+c_start_switch (location_t switch_loc,
+ location_t switch_cond_loc,
+ tree exp, bool explicit_cast_p)
 {
   tree orig_type = error_mark_node;
   bool bool_cond_p = false;
@@ -10780,18 +10783,19 @@ c_start_case (location_t switch_loc,
  }
     }
 
-  /* Add this new SWITCH_EXPR to the stack.  */
+  /* Add this new SWITCH_STMT to the stack.  */
   cs = XNEW (struct c_switch);
-  cs->switch_expr = build2 (SWITCH_EXPR, orig_type, exp, NULL_TREE);
-  SET_EXPR_LOCATION (cs->switch_expr, switch_loc);
+  cs->switch_stmt = build_stmt (switch_loc, SWITCH_STMT, exp,
+ NULL_TREE, orig_type, NULL_TREE);
   cs->orig_type = orig_type;
   cs->cases = splay_tree_new (case_compare, NULL, NULL);
   cs->bindings = c_get_switch_bindings ();
+  cs->break_stmt_seen_p = false;
   cs->bool_cond_p = bool_cond_p;
   cs->next = c_switch_stack;
   c_switch_stack = cs;
 
-  return add_stmt (cs->switch_expr);
+  return add_stmt (cs->switch_stmt);
 }
 
 /* Process a case label at location LOC.  */
@@ -10827,12 +10831,12 @@ do_case (location_t loc, tree low_value, tree high_value)
     }
 
   if (c_check_switch_jump_warnings (c_switch_stack->bindings,
-    EXPR_LOCATION (c_switch_stack->switch_expr),
+    EXPR_LOCATION (c_switch_stack->switch_stmt),
     loc))
     return NULL_TREE;
 
   label = c_add_case_label (loc, c_switch_stack->cases,
-    SWITCH_COND (c_switch_stack->switch_expr),
+    SWITCH_STMT_COND (c_switch_stack->switch_stmt),
     low_value, high_value);
   if (label == error_mark_node)
     label = NULL_TREE;
@@ -10843,20 +10847,22 @@ do_case (location_t loc, tree low_value, tree high_value)
    controlling expression of the switch, or NULL_TREE.  */
 
 void
-c_finish_case (tree body, tree type)
+c_finish_switch (tree body, tree type)
 {
   struct c_switch *cs = c_switch_stack;
   location_t switch_location;
 
-  SWITCH_BODY (cs->switch_expr) = body;
+  SWITCH_STMT_BODY (cs->switch_stmt) = body;
 
   /* Emit warnings as needed.  */
-  switch_location = EXPR_LOCATION (cs->switch_expr);
+  switch_location = EXPR_LOCATION (cs->switch_stmt);
   c_do_switch_warnings (cs->cases, switch_location,
- type ? type : TREE_TYPE (cs->switch_expr),
- SWITCH_COND (cs->switch_expr), cs->bool_cond_p);
-  if (c_switch_covers_all_cases_p (cs->cases, TREE_TYPE (cs->switch_expr)))
-    SWITCH_ALL_CASES_P (cs->switch_expr) = 1;
+ type ? type : SWITCH_STMT_TYPE (cs->switch_stmt),
+ SWITCH_STMT_COND (cs->switch_stmt), cs->bool_cond_p);
+  if (c_switch_covers_all_cases_p (cs->cases,
+   SWITCH_STMT_TYPE (cs->switch_stmt)))
+    SWITCH_STMT_ALL_CASES_P (cs->switch_stmt) = 1;
+  SWITCH_STMT_NO_BREAK_P (cs->switch_stmt) = !cs->break_stmt_seen_p;
 
   /* Pop the stack.  */
   c_switch_stack = cs->next;
@@ -10880,110 +10886,9 @@ c_finish_if_stmt (location_t if_locus, tree cond, tree then_block,
   add_stmt (stmt);
 }
 
-/* Emit a general-purpose loop construct.  START_LOCUS is the location of
-   the beginning of the loop.  COND is the loop condition.  COND_IS_FIRST
-   is false for DO loops.  INCR is the FOR increment expression.  BODY is
-   the statement controlled by the loop.  BLAB is the break label.  CLAB is
-   the continue label.  Everything is allowed to be NULL.
-   COND_LOCUS is the location of the loop condition, INCR_LOCUS is the
-   location of the FOR increment expression.  */
-
-void
-c_finish_loop (location_t start_locus, location_t cond_locus, tree cond,
-       location_t incr_locus, tree incr, tree body, tree blab,
-       tree clab, bool cond_is_first)
-{
-  tree entry = NULL, exit = NULL, t;
-
-  /* If the condition is zero don't generate a loop construct.  */
-  if (cond && integer_zerop (cond))
-    {
-      if (cond_is_first)
- {
-  t = build_and_jump (&blab);
-  SET_EXPR_LOCATION (t, start_locus);
-  add_stmt (t);
- }
-    }
-  else
-    {
-      tree top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
-
-      /* If we have an exit condition, then we build an IF with gotos either
- out of the loop, or to the top of it.  If there's no exit condition,
- then we just build a jump back to the top.  */
-      exit = build_and_jump (&LABEL_EXPR_LABEL (top));
-
-      if (cond && !integer_nonzerop (cond))
- {
-  /* Canonicalize the loop condition to the end.  This means
-     generating a branch to the loop condition.  Reuse the
-     continue label, if possible.  */
-  if (cond_is_first)
-    {
-      if (incr || !clab)
- {
-  entry = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
-  t = build_and_jump (&LABEL_EXPR_LABEL (entry));
- }
-      else
- t = build1 (GOTO_EXPR, void_type_node, clab);
-      SET_EXPR_LOCATION (t, start_locus);
-      add_stmt (t);
-    }
-
-  t = build_and_jump (&blab);
-  exit = fold_build3_loc (cond_is_first ? start_locus : input_location,
-  COND_EXPR, void_type_node, cond, exit, t);
- }
-      else
- {
-  /* For the backward-goto's location of an unconditional loop
-     use the beginning of the body, or, if there is none, the
-     top of the loop.  */
-  location_t loc = EXPR_LOCATION (expr_first (body));
-  if (loc == UNKNOWN_LOCATION)
-    loc = start_locus;
-  SET_EXPR_LOCATION (exit, loc);
- }
-
-      add_stmt (top);
-    }
-
-  if (body)
-    add_stmt (body);
-  if (clab)
-    add_stmt (build1 (LABEL_EXPR, void_type_node, clab));
-  if (incr)
-    {
-      if (MAY_HAVE_DEBUG_MARKER_STMTS && incr_locus != UNKNOWN_LOCATION)
- {
-  t = build0 (DEBUG_BEGIN_STMT, void_type_node);
-  SET_EXPR_LOCATION (t, incr_locus);
-  add_stmt (t);
- }
-      add_stmt (incr);
-    }
-  if (entry)
-    add_stmt (entry);
-  if (MAY_HAVE_DEBUG_MARKER_STMTS && cond_locus != UNKNOWN_LOCATION)
-    {
-      t = build0 (DEBUG_BEGIN_STMT, void_type_node);
-      SET_EXPR_LOCATION (t, cond_locus);
-      add_stmt (t);
-    }
-  if (exit)
-    add_stmt (exit);
-  if (blab)
-    add_stmt (build1 (LABEL_EXPR, void_type_node, blab));
-}
-
 tree
-c_finish_bc_stmt (location_t loc, tree *label_p, bool is_break)
+c_finish_bc_stmt (location_t loc, tree label, bool is_break)
 {
-  bool skip;
-  tree label = *label_p;
-
   /* In switch statements break is sometimes stylistically used after
      a return statement.  This can lead to spurious warnings about
      control reaching the end of a non-void function when it is
@@ -10991,47 +10896,55 @@ c_finish_bc_stmt (location_t loc, tree *label_p, bool is_break)
      language specific tree nodes; this works because
      block_may_fallthru returns true when given something it does not
      understand.  */
-  skip = !block_may_fallthru (cur_stmt_list);
+  bool skip = !block_may_fallthru (cur_stmt_list);
 
-  if (!label)
-    {
-      if (!skip)
- *label_p = label = create_artificial_label (loc);
-    }
-  else if (TREE_CODE (label) == LABEL_DECL)
-    ;
-  else switch (TREE_INT_CST_LOW (label))
-    {
-    case 0:
-      if (is_break)
+  if (is_break)
+    switch (in_statement)
+      {
+      case 0:
  error_at (loc, "break statement not within loop or switch");
-      else
+ return NULL_TREE;
+      case IN_OMP_BLOCK:
+ error_at (loc, "invalid exit from OpenMP structured block");
+ return NULL_TREE;
+      case IN_OMP_FOR:
+ error_at (loc, "break statement used with OpenMP for loop");
+ return NULL_TREE;
+      case IN_ITERATION_STMT:
+      case IN_OBJC_FOREACH:
+ break;
+      default:
+ gcc_assert (in_statement & IN_SWITCH_STMT);
+ c_switch_stack->break_stmt_seen_p = true;
+ break;
+      }
+  else
+    switch (in_statement & ~IN_SWITCH_STMT)
+      {
+      case 0:
  error_at (loc, "continue statement not within a loop");
-      return NULL_TREE;
-
-    case 1:
-      gcc_assert (is_break);
-      error_at (loc, "break statement used with OpenMP for loop");
-      return NULL_TREE;
-
-    case 2:
-      if (is_break)
- error ("break statement within %<#pragma simd%> loop body");
-      else
- error ("continue statement within %<#pragma simd%> loop body");
-      return NULL_TREE;
-
-    default:
-      gcc_unreachable ();
-    }
+ return NULL_TREE;
+      case IN_OMP_BLOCK:
+ error_at (loc, "invalid exit from OpenMP structured block");
+ return NULL_TREE;
+      case IN_ITERATION_STMT:
+      case IN_OMP_FOR:
+      case IN_OBJC_FOREACH:
+ break;
+      default:
+ gcc_unreachable ();
+      }
 
   if (skip)
     return NULL_TREE;
-
-  if (!is_break)
-    add_stmt (build_predict_expr (PRED_CONTINUE, NOT_TAKEN));
-
-  return add_stmt (build1 (GOTO_EXPR, void_type_node, label));
+  else if (in_statement & IN_OBJC_FOREACH)
+    {
+      /* The foreach expander produces low-level code using gotos instead
+ of a structured loop construct.  */
+      gcc_assert (label);
+      return add_stmt (build_stmt (loc, GOTO_EXPR, label));
+    }
+  return add_stmt (build_stmt (loc, (is_break ? BREAK_STMT : CONTINUE_STMT)));
 }
 
 /* A helper routine for c_process_expr_stmt and c_finish_stmt_expr.  */
diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c
index 6c18804..55c34d7 100644
--- a/gcc/objc/objc-act.c
+++ b/gcc/objc/objc-act.c
@@ -2050,10 +2050,8 @@ objc_start_method_definition (bool is_class_method, tree decl, tree attributes,
     return false;
 
 #ifndef OBJCPLUS
-  /* Indicate no valid break/continue context by setting these variables
-     to some non-null, non-label value.  We'll notice and emit the proper
-     error message in c_finish_bc_stmt.  */
-  c_break_label = c_cont_label = size_zero_node;
+  /* Indicate no valid break/continue context.  */
+  in_statement = 0;
 #endif
 
   if (attributes)
diff --git a/gcc/testsuite/gcc.dg/gomp/block-7.c b/gcc/testsuite/gcc.dg/gomp/block-7.c
index 6219e7e..3e87464 100644
--- a/gcc/testsuite/gcc.dg/gomp/block-7.c
+++ b/gcc/testsuite/gcc.dg/gomp/block-7.c
@@ -6,15 +6,15 @@ void foo()
   for (i = 0; i < 10; ++i)
     {
       #pragma omp for
-      for (j = ({ continue; 0; }); // { dg-error "invalid branch to/from OpenMP structured block" }
-   j < ({ continue; 10; }); // { dg-error "invalid branch to/from OpenMP structured block" }
-   j += ({ continue; 1; })) // { dg-error "invalid branch to/from OpenMP structured block" }
+      for (j = ({ continue; 0; }); // { dg-error "invalid exit from OpenMP structured block" }
+   j < ({ continue; 10; }); // { dg-error "invalid exit from OpenMP structured block" }
+   j += ({ continue; 1; })) // { dg-error "invalid exit from OpenMP structured block" }
  continue;
 
       #pragma omp for
-      for (j = ({ break; 0; }); // { dg-error "invalid branch to/from OpenMP structured block" }
-   j < ({ break; 10; }); // { dg-error "invalid branch to/from OpenMP structured block" }
-   j += ({ break; 1; })) // { dg-error "invalid branch to/from OpenMP structured block" }
+      for (j = ({ break; 0; }); // { dg-error "invalid exit from OpenMP structured block" }
+   j < ({ break; 10; }); // { dg-error "invalid exit from OpenMP structured block" }
+   j += ({ break; 1; })) // { dg-error "invalid exit from OpenMP structured block" }
  break; // { dg-error "break" }
     }
 }
--
2.8.1

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 0/2] Make C front end share the C++ tree representation of loops and switches

Sandra Loosemore
In reply to this post by Sandra Loosemore
Ping!

On 11/13/19 9:27 AM, Sandra Loosemore wrote:

> This patch series lays some groundwork for the project to redo the
> OpenACC "kernels" region support in GCC, described in Thomas
> Schwinge's recent talk at the GNU Cauldron:
>
> https://gcc.gnu.org/wiki/cauldron2019talks?action=AttachFile&do=view&target=OpenACC+kernels-cauldron2019.pdf
>
> Briefly, the larger goal is to make the compiler recognize "for" loops
> that are candidates for parallelization, and treat them as if they
> were annotated with "#pragma acc loop auto".  This is fairly
> straightforward to do on the output of the Fortran and C++ front ends,
> which both produce a high-level parse tree representation of the loop.
> OTOH, the C front end currently lowers loops into a goto form very
> early, making it hard both to recognize "for" loops and to
> differentiate between (valid) structured and (invalid) unstructured
> control flow in the body of the loop.
>
> So, the immediate goal of this patch sequence is to make the C front
> end produce output using the same high-level tree representation as
> the C++ front end already emits: specifically, to produce FOR_STMT,
> WHILE_STMT, and DO_STMT for loops, BREAK_STMT and CONTINUE_STMT
> instead of gotos, and also SWITCH_STMT instead of SWITCH_EXPR since
> that's mixed up in the handling for "break".  Besides this being
> helpful to OpenACC implementation, there's also some argument to be
> made that sharing more code between the respective C family front ends
> is good from an engineering perspective, etc.
>
> Part 1 of the patch set moves the definitions and support
> (genericization, pretty-printers, dumpers, etc) for those tree nodes
> out of the cp/ front end and into the common c-family/ code.  Part 2
> hacks up the C front end to emit the now-shared data structures.
>
> I bootstrapped and regression-tested this on x86_64-linux-gnu.  There
> are a few regressions involving these tests:
>
> gcc.dg/tree-ssa/pr77445-2.c
> gcc.dg/tree-ssa/ssa-dce-3.c
> gcc.dg/tree-ssa/ssa-dom-thread-7.c
>
> I looked at these briefly and it seems like the problem here is that
> the output of the C front end after gimplification is different than
> it used to be, since I swapped in the C++ genericization code (it
> lowers to a LOOP_EXPR instead of directly to goto form, then the
> LOOP_EXPR gets lowered during gimplification).  TBH, I don't really
> understand what optimizations these test cases are trying to test, and
> whether the pattern-matching is too fragile, whether the form of the code
> isn't something that actually tests the optimization, or whether the
> optimization is just not working on this input.  So I'm not sure what
> to do about those failures!  :-(  I could change the genericizer to
> emit the same goto form that the C front end formerly emitted instead
> of the C++-style output, but that might just be papering over bugs
> elsewhere.  Any advice on how to proceed here is welcome.  If the
> patches are OK otherwise, maybe just file bugzilla issues for these
> regressions?
>
> -Sandra
>
>
> Sandra Loosemore (2):
>    Move loop and switch tree data structures from cp/ to c-family/.
>    Change C front end to emit structured loop and switch tree nodes.
>
>   gcc/c-family/c-common.c             |  24 +++
>   gcc/c-family/c-common.def           |  24 +++
>   gcc/c-family/c-common.h             |  53 ++++-
>   gcc/c-family/c-dump.c               |  38 ++++
>   gcc/c-family/c-gimplify.c           | 414 ++++++++++++++++++++++++++++++++++++
>   gcc/c-family/c-pretty-print.c       |  92 +++++++-
>   gcc/c/c-decl.c                      |  18 +-
>   gcc/c/c-lang.h                      |   3 +-
>   gcc/c/c-objc-common.h               |   2 +
>   gcc/c/c-parser.c                    | 125 ++++++-----
>   gcc/c/c-tree.h                      |  21 +-
>   gcc/c/c-typeck.c                    | 227 ++++++--------------
>   gcc/cp/cp-gimplify.c                | 347 ++----------------------------
>   gcc/cp/cp-objcp-common.c            |  13 +-
>   gcc/cp/cp-tree.def                  |  23 --
>   gcc/cp/cp-tree.h                    |  40 ----
>   gcc/cp/cxx-pretty-print.c           |  78 -------
>   gcc/cp/dump.c                       |  31 ---
>   gcc/doc/generic.texi                |  56 +++--
>   gcc/objc/objc-act.c                 |   6 +-
>   gcc/testsuite/gcc.dg/gomp/block-7.c |  12 +-
>   21 files changed, 859 insertions(+), 788 deletions(-)
>

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 0/2] Make C front end share the C++ tree representation of loops and switches

Jeff Law
In reply to this post by Sandra Loosemore
On Wed, 2019-11-13 at 09:27 -0700, Sandra Loosemore wrote:
>
> I bootstrapped and regression-tested this on x86_64-linux-gnu.  There
> are a few regressions involving these tests:
>
> gcc.dg/tree-ssa/pr77445-2.c
I believe tihs is another instance of the FSA optimization.  I'd need
to see the before/after dumps to know if it's regressed.  The main
purpose of the test was to verify that we didn't muck up the profile
estimation after the FSA optimization.


> gcc.dg/tree-ssa/ssa-dce-3.c
So I think this one is supposed to collapse into a trivial infinite
loop.  Anything else would be a regression.


> gcc.dg/tree-ssa/ssa-dom-thread-7.c
This is the FSA optimization.  Unfortunately checking for it being done
right is exceedingly painful.  If you pass along the before/after dumps
I can probably help determine whether or not we just need an update to
the scanned bits.

Given how much pressure there was to handle the FSA optimization, I'd
prefer to make sure we're still doing it before moving forward.

jeff