xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/graph.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
11debfc3dSmrg /* Output routines for graphical representation.
2*8feb0f0bSmrg    Copyright (C) 1998-2020 Free Software Foundation, Inc.
31debfc3dSmrg    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
41debfc3dSmrg    Rewritten for DOT output by Steven Bosscher, 2012.
51debfc3dSmrg 
61debfc3dSmrg This file is part of GCC.
71debfc3dSmrg 
81debfc3dSmrg GCC is free software; you can redistribute it and/or modify it under
91debfc3dSmrg the terms of the GNU General Public License as published by the Free
101debfc3dSmrg Software Foundation; either version 3, or (at your option) any later
111debfc3dSmrg version.
121debfc3dSmrg 
131debfc3dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
141debfc3dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
151debfc3dSmrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
161debfc3dSmrg for more details.
171debfc3dSmrg 
181debfc3dSmrg You should have received a copy of the GNU General Public License
191debfc3dSmrg along with GCC; see the file COPYING3.  If not see
201debfc3dSmrg <http://www.gnu.org/licenses/>.  */
211debfc3dSmrg 
221debfc3dSmrg #include "config.h"
231debfc3dSmrg #include "system.h"
241debfc3dSmrg #include "coretypes.h"
251debfc3dSmrg #include "backend.h"
261debfc3dSmrg #include "cfghooks.h"
271debfc3dSmrg #include "pretty-print.h"
281debfc3dSmrg #include "diagnostic-core.h" /* for fatal_error */
291debfc3dSmrg #include "cfganal.h"
301debfc3dSmrg #include "cfgloop.h"
311debfc3dSmrg #include "graph.h"
321debfc3dSmrg #include "dumpfile.h"
331debfc3dSmrg 
341debfc3dSmrg /* DOT files with the .dot extension are recognized as document templates
351debfc3dSmrg    by a well-known piece of word processing software out of Redmond, WA.
361debfc3dSmrg    Therefore some recommend using the .gv extension instead.  Obstinately
371debfc3dSmrg    ignore that recommendation...  */
381debfc3dSmrg static const char *const graph_ext = ".dot";
391debfc3dSmrg 
401debfc3dSmrg /* Open a file with MODE for dumping our graph to.
411debfc3dSmrg    Return the file pointer.  */
421debfc3dSmrg static FILE *
open_graph_file(const char * base,const char * mode)431debfc3dSmrg open_graph_file (const char *base, const char *mode)
441debfc3dSmrg {
451debfc3dSmrg   size_t namelen = strlen (base);
461debfc3dSmrg   size_t extlen = strlen (graph_ext) + 1;
471debfc3dSmrg   char *buf = XALLOCAVEC (char, namelen + extlen);
481debfc3dSmrg   FILE *fp;
491debfc3dSmrg 
501debfc3dSmrg   memcpy (buf, base, namelen);
511debfc3dSmrg   memcpy (buf + namelen, graph_ext, extlen);
521debfc3dSmrg 
531debfc3dSmrg   fp = fopen (buf, mode);
541debfc3dSmrg   if (fp == NULL)
55*8feb0f0bSmrg     fatal_error (input_location, "cannot open %s: %m", buf);
561debfc3dSmrg 
571debfc3dSmrg   return fp;
581debfc3dSmrg }
591debfc3dSmrg 
60*8feb0f0bSmrg /* Disable warnings about quoting issues in the pp_xxx calls below
61*8feb0f0bSmrg    that (intentionally) don't follow GCC diagnostic conventions.  */
62*8feb0f0bSmrg #if __GNUC__ >= 10
63*8feb0f0bSmrg #  pragma GCC diagnostic push
64*8feb0f0bSmrg #  pragma GCC diagnostic ignored "-Wformat-diag"
65*8feb0f0bSmrg #endif
66*8feb0f0bSmrg 
671debfc3dSmrg /* Draw a basic block BB belonging to the function with FUNCDEF_NO
681debfc3dSmrg    as its unique number.  */
691debfc3dSmrg static void
draw_cfg_node(pretty_printer * pp,int funcdef_no,basic_block bb)701debfc3dSmrg draw_cfg_node (pretty_printer *pp, int funcdef_no, basic_block bb)
711debfc3dSmrg {
721debfc3dSmrg   const char *shape;
731debfc3dSmrg   const char *fillcolor;
741debfc3dSmrg 
751debfc3dSmrg   if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK)
761debfc3dSmrg     {
771debfc3dSmrg       shape = "Mdiamond";
781debfc3dSmrg       fillcolor = "white";
791debfc3dSmrg     }
801debfc3dSmrg   else
811debfc3dSmrg     {
821debfc3dSmrg       shape = "record";
831debfc3dSmrg       fillcolor =
841debfc3dSmrg 	BB_PARTITION (bb) == BB_HOT_PARTITION ? "lightpink"
851debfc3dSmrg 	: BB_PARTITION (bb) == BB_COLD_PARTITION ? "lightblue"
861debfc3dSmrg 	: "lightgrey";
871debfc3dSmrg     }
881debfc3dSmrg 
891debfc3dSmrg   pp_printf (pp,
901debfc3dSmrg 	     "\tfn_%d_basic_block_%d "
911debfc3dSmrg 	     "[shape=%s,style=filled,fillcolor=%s,label=\"",
921debfc3dSmrg 	     funcdef_no, bb->index, shape, fillcolor);
931debfc3dSmrg 
941debfc3dSmrg   if (bb->index == ENTRY_BLOCK)
951debfc3dSmrg     pp_string (pp, "ENTRY");
961debfc3dSmrg   else if (bb->index == EXIT_BLOCK)
971debfc3dSmrg     pp_string (pp, "EXIT");
981debfc3dSmrg   else
991debfc3dSmrg     {
1001debfc3dSmrg       pp_left_brace (pp);
1011debfc3dSmrg       pp_write_text_to_stream (pp);
1021debfc3dSmrg       dump_bb_for_graph (pp, bb);
1031debfc3dSmrg       pp_right_brace (pp);
1041debfc3dSmrg     }
1051debfc3dSmrg 
1061debfc3dSmrg   pp_string (pp, "\"];\n\n");
1071debfc3dSmrg   pp_flush (pp);
1081debfc3dSmrg }
1091debfc3dSmrg 
1101debfc3dSmrg /* Draw all successor edges of a basic block BB belonging to the function
1111debfc3dSmrg    with FUNCDEF_NO as its unique number.  */
1121debfc3dSmrg static void
draw_cfg_node_succ_edges(pretty_printer * pp,int funcdef_no,basic_block bb)1131debfc3dSmrg draw_cfg_node_succ_edges (pretty_printer *pp, int funcdef_no, basic_block bb)
1141debfc3dSmrg {
1151debfc3dSmrg   edge e;
1161debfc3dSmrg   edge_iterator ei;
1171debfc3dSmrg   FOR_EACH_EDGE (e, ei, bb->succs)
1181debfc3dSmrg     {
1191debfc3dSmrg       const char *style = "\"solid,bold\"";
1201debfc3dSmrg       const char *color = "black";
1211debfc3dSmrg       int weight = 10;
1221debfc3dSmrg 
1231debfc3dSmrg       if (e->flags & EDGE_FAKE)
1241debfc3dSmrg 	{
1251debfc3dSmrg 	  style = "dotted";
1261debfc3dSmrg 	  color = "green";
1271debfc3dSmrg 	  weight = 0;
1281debfc3dSmrg 	}
1291debfc3dSmrg       else if (e->flags & EDGE_DFS_BACK)
1301debfc3dSmrg 	{
1311debfc3dSmrg 	  style = "\"dotted,bold\"";
1321debfc3dSmrg 	  color = "blue";
1331debfc3dSmrg 	  weight = 10;
1341debfc3dSmrg 	}
1351debfc3dSmrg       else if (e->flags & EDGE_FALLTHRU)
1361debfc3dSmrg 	{
1371debfc3dSmrg 	  color = "blue";
1381debfc3dSmrg 	  weight = 100;
1391debfc3dSmrg 	}
1401debfc3dSmrg 
1411debfc3dSmrg       if (e->flags & EDGE_ABNORMAL)
1421debfc3dSmrg 	color = "red";
1431debfc3dSmrg 
1441debfc3dSmrg       pp_printf (pp,
1451debfc3dSmrg 		 "\tfn_%d_basic_block_%d:s -> fn_%d_basic_block_%d:n "
146a2dc1f3fSmrg 		 "[style=%s,color=%s,weight=%d,constraint=%s",
1471debfc3dSmrg 		 funcdef_no, e->src->index,
1481debfc3dSmrg 		 funcdef_no, e->dest->index,
1491debfc3dSmrg 		 style, color, weight,
150a2dc1f3fSmrg 		 (e->flags & (EDGE_FAKE | EDGE_DFS_BACK)) ? "false" : "true");
151a2dc1f3fSmrg       if (e->probability.initialized_p ())
152a2dc1f3fSmrg         pp_printf (pp, ",label=\"[%i%%]\"",
153a2dc1f3fSmrg 		   e->probability.to_reg_br_prob_base ()
154a2dc1f3fSmrg 		   * 100 / REG_BR_PROB_BASE);
155a2dc1f3fSmrg       pp_printf (pp, "];\n");
1561debfc3dSmrg     }
1571debfc3dSmrg   pp_flush (pp);
1581debfc3dSmrg }
1591debfc3dSmrg 
1601debfc3dSmrg /* Draw all the basic blocks in the CFG in case loops are not available.
1611debfc3dSmrg    First compute a topological order of the blocks to get a good ranking of
1621debfc3dSmrg    the nodes.  Then, if any nodes are not reachable from ENTRY, add them at
1631debfc3dSmrg    the end.  */
1641debfc3dSmrg 
1651debfc3dSmrg static void
draw_cfg_nodes_no_loops(pretty_printer * pp,struct function * fun)1661debfc3dSmrg draw_cfg_nodes_no_loops (pretty_printer *pp, struct function *fun)
1671debfc3dSmrg {
1681debfc3dSmrg   int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fun));
1691debfc3dSmrg   int i, n;
1701debfc3dSmrg 
1711debfc3dSmrg   auto_sbitmap visited (last_basic_block_for_fn (cfun));
1721debfc3dSmrg   bitmap_clear (visited);
1731debfc3dSmrg 
1741debfc3dSmrg   n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, true);
1751debfc3dSmrg   for (i = n_basic_blocks_for_fn (fun) - n;
1761debfc3dSmrg        i < n_basic_blocks_for_fn (fun); i++)
1771debfc3dSmrg     {
1781debfc3dSmrg       basic_block bb = BASIC_BLOCK_FOR_FN (cfun, rpo[i]);
1791debfc3dSmrg       draw_cfg_node (pp, fun->funcdef_no, bb);
1801debfc3dSmrg       bitmap_set_bit (visited, bb->index);
1811debfc3dSmrg     }
1821debfc3dSmrg   free (rpo);
1831debfc3dSmrg 
1841debfc3dSmrg   if (n != n_basic_blocks_for_fn (fun))
1851debfc3dSmrg     {
1861debfc3dSmrg       /* Some blocks are unreachable.  We still want to dump them.  */
1871debfc3dSmrg       basic_block bb;
1881debfc3dSmrg       FOR_ALL_BB_FN (bb, fun)
1891debfc3dSmrg 	if (! bitmap_bit_p (visited, bb->index))
1901debfc3dSmrg 	  draw_cfg_node (pp, fun->funcdef_no, bb);
1911debfc3dSmrg     }
1921debfc3dSmrg }
1931debfc3dSmrg 
1941debfc3dSmrg /* Draw all the basic blocks in LOOP.  Print the blocks in breath-first
1951debfc3dSmrg    order to get a good ranking of the nodes.  This function is recursive:
1961debfc3dSmrg    It first prints inner loops, then the body of LOOP itself.  */
1971debfc3dSmrg 
1981debfc3dSmrg static void
draw_cfg_nodes_for_loop(pretty_printer * pp,int funcdef_no,class loop * loop)1991debfc3dSmrg draw_cfg_nodes_for_loop (pretty_printer *pp, int funcdef_no,
200*8feb0f0bSmrg 			 class loop *loop)
2011debfc3dSmrg {
2021debfc3dSmrg   basic_block *body;
2031debfc3dSmrg   unsigned int i;
2041debfc3dSmrg   const char *fillcolors[3] = { "grey88", "grey77", "grey66" };
2051debfc3dSmrg 
2061debfc3dSmrg   if (loop->header != NULL
2071debfc3dSmrg       && loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
2081debfc3dSmrg     pp_printf (pp,
2091debfc3dSmrg 	       "\tsubgraph cluster_%d_%d {\n"
2101debfc3dSmrg 	       "\tstyle=\"filled\";\n"
2111debfc3dSmrg 	       "\tcolor=\"darkgreen\";\n"
2121debfc3dSmrg 	       "\tfillcolor=\"%s\";\n"
2131debfc3dSmrg 	       "\tlabel=\"loop %d\";\n"
2141debfc3dSmrg 	       "\tlabeljust=l;\n"
2151debfc3dSmrg 	       "\tpenwidth=2;\n",
2161debfc3dSmrg 	       funcdef_no, loop->num,
2171debfc3dSmrg 	       fillcolors[(loop_depth (loop) - 1) % 3],
2181debfc3dSmrg 	       loop->num);
2191debfc3dSmrg 
220*8feb0f0bSmrg   for (class loop *inner = loop->inner; inner; inner = inner->next)
2211debfc3dSmrg     draw_cfg_nodes_for_loop (pp, funcdef_no, inner);
2221debfc3dSmrg 
2231debfc3dSmrg   if (loop->header == NULL)
2241debfc3dSmrg     return;
2251debfc3dSmrg 
2261debfc3dSmrg   if (loop->latch == EXIT_BLOCK_PTR_FOR_FN (cfun))
2271debfc3dSmrg     body = get_loop_body (loop);
2281debfc3dSmrg   else
2291debfc3dSmrg     body = get_loop_body_in_bfs_order (loop);
2301debfc3dSmrg 
2311debfc3dSmrg   for (i = 0; i < loop->num_nodes; i++)
2321debfc3dSmrg     {
2331debfc3dSmrg       basic_block bb = body[i];
2341debfc3dSmrg       if (bb->loop_father == loop)
2351debfc3dSmrg 	draw_cfg_node (pp, funcdef_no, bb);
2361debfc3dSmrg     }
2371debfc3dSmrg 
2381debfc3dSmrg   free (body);
2391debfc3dSmrg 
2401debfc3dSmrg   if (loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
2411debfc3dSmrg     pp_printf (pp, "\t}\n");
2421debfc3dSmrg }
2431debfc3dSmrg 
2441debfc3dSmrg /* Draw all the basic blocks in the CFG in case the loop tree is available.
2451debfc3dSmrg    All loop bodys are printed in clusters.  */
2461debfc3dSmrg 
2471debfc3dSmrg static void
draw_cfg_nodes(pretty_printer * pp,struct function * fun)2481debfc3dSmrg draw_cfg_nodes (pretty_printer *pp, struct function *fun)
2491debfc3dSmrg {
2501debfc3dSmrg   if (loops_for_fn (fun))
2511debfc3dSmrg     draw_cfg_nodes_for_loop (pp, fun->funcdef_no, get_loop (fun, 0));
2521debfc3dSmrg   else
2531debfc3dSmrg     draw_cfg_nodes_no_loops (pp, fun);
2541debfc3dSmrg }
2551debfc3dSmrg 
2561debfc3dSmrg /* Draw all edges in the CFG.  Retreating edges are drawin as not
257a2dc1f3fSmrg    constraining, this makes the layout of the graph better.  */
2581debfc3dSmrg 
2591debfc3dSmrg static void
draw_cfg_edges(pretty_printer * pp,struct function * fun)2601debfc3dSmrg draw_cfg_edges (pretty_printer *pp, struct function *fun)
2611debfc3dSmrg {
2621debfc3dSmrg   basic_block bb;
263a2dc1f3fSmrg 
264a2dc1f3fSmrg   /* Save EDGE_DFS_BACK flag to dfs_back.  */
265a2dc1f3fSmrg   auto_bitmap dfs_back;
266a2dc1f3fSmrg   edge e;
267a2dc1f3fSmrg   edge_iterator ei;
268a2dc1f3fSmrg   unsigned int idx = 0;
269a2dc1f3fSmrg   FOR_EACH_BB_FN (bb, cfun)
270a2dc1f3fSmrg     FOR_EACH_EDGE (e, ei, bb->succs)
271a2dc1f3fSmrg       {
272a2dc1f3fSmrg 	if (e->flags & EDGE_DFS_BACK)
273a2dc1f3fSmrg 	  bitmap_set_bit (dfs_back, idx);
274a2dc1f3fSmrg 	idx++;
275a2dc1f3fSmrg       }
276a2dc1f3fSmrg 
2771debfc3dSmrg   mark_dfs_back_edges ();
2781debfc3dSmrg   FOR_ALL_BB_FN (bb, cfun)
2791debfc3dSmrg     draw_cfg_node_succ_edges (pp, fun->funcdef_no, bb);
2801debfc3dSmrg 
281a2dc1f3fSmrg   /* Restore EDGE_DFS_BACK flag from dfs_back.  */
282a2dc1f3fSmrg   idx = 0;
283a2dc1f3fSmrg   FOR_EACH_BB_FN (bb, cfun)
284a2dc1f3fSmrg     FOR_EACH_EDGE (e, ei, bb->succs)
285a2dc1f3fSmrg       {
286a2dc1f3fSmrg 	if (bitmap_bit_p (dfs_back, idx))
287a2dc1f3fSmrg 	  e->flags |= EDGE_DFS_BACK;
288a2dc1f3fSmrg 	else
289a2dc1f3fSmrg 	  e->flags &= ~EDGE_DFS_BACK;
290a2dc1f3fSmrg 	idx++;
291a2dc1f3fSmrg       }
292a2dc1f3fSmrg 
2931debfc3dSmrg   /* Add an invisible edge from ENTRY to EXIT, to improve the graph layout.  */
2941debfc3dSmrg   pp_printf (pp,
2951debfc3dSmrg 	     "\tfn_%d_basic_block_%d:s -> fn_%d_basic_block_%d:n "
2961debfc3dSmrg 	     "[style=\"invis\",constraint=true];\n",
2971debfc3dSmrg 	     fun->funcdef_no, ENTRY_BLOCK,
2981debfc3dSmrg 	     fun->funcdef_no, EXIT_BLOCK);
2991debfc3dSmrg   pp_flush (pp);
3001debfc3dSmrg }
3011debfc3dSmrg 
3021debfc3dSmrg /* Print a graphical representation of the CFG of function FUN.
3031debfc3dSmrg    First print all basic blocks.  Draw all edges at the end to get
3041debfc3dSmrg    subgraphs right for GraphViz, which requires nodes to be defined
3051debfc3dSmrg    before edges to cluster nodes properly.  */
3061debfc3dSmrg 
3071debfc3dSmrg void DEBUG_FUNCTION
print_graph_cfg(FILE * fp,struct function * fun)3081debfc3dSmrg print_graph_cfg (FILE *fp, struct function *fun)
3091debfc3dSmrg {
3101debfc3dSmrg   pretty_printer graph_slim_pp;
3111debfc3dSmrg   graph_slim_pp.buffer->stream = fp;
3121debfc3dSmrg   pretty_printer *const pp = &graph_slim_pp;
3131debfc3dSmrg   const char *funcname = function_name (fun);
3141debfc3dSmrg   pp_printf (pp, "subgraph \"cluster_%s\" {\n"
3151debfc3dSmrg 		 "\tstyle=\"dashed\";\n"
3161debfc3dSmrg 		 "\tcolor=\"black\";\n"
3171debfc3dSmrg 		 "\tlabel=\"%s ()\";\n",
3181debfc3dSmrg 		 funcname, funcname);
3191debfc3dSmrg   draw_cfg_nodes (pp, fun);
3201debfc3dSmrg   draw_cfg_edges (pp, fun);
3211debfc3dSmrg   pp_printf (pp, "}\n");
3221debfc3dSmrg   pp_flush (pp);
3231debfc3dSmrg }
3241debfc3dSmrg 
3251debfc3dSmrg /* Overload with additional flag argument.  */
3261debfc3dSmrg 
3271debfc3dSmrg void DEBUG_FUNCTION
print_graph_cfg(FILE * fp,struct function * fun,dump_flags_t flags)328a2dc1f3fSmrg print_graph_cfg (FILE *fp, struct function *fun, dump_flags_t flags)
3291debfc3dSmrg {
330a2dc1f3fSmrg   dump_flags_t saved_dump_flags = dump_flags;
3311debfc3dSmrg   dump_flags = flags;
3321debfc3dSmrg   print_graph_cfg (fp, fun);
3331debfc3dSmrg   dump_flags = saved_dump_flags;
3341debfc3dSmrg }
3351debfc3dSmrg 
3361debfc3dSmrg 
3371debfc3dSmrg /* Print a graphical representation of the CFG of function FUN.
3381debfc3dSmrg    First print all basic blocks.  Draw all edges at the end to get
3391debfc3dSmrg    subgraphs right for GraphViz, which requires nodes to be defined
3401debfc3dSmrg    before edges to cluster nodes properly.  */
3411debfc3dSmrg 
3421debfc3dSmrg void
print_graph_cfg(const char * base,struct function * fun)3431debfc3dSmrg print_graph_cfg (const char *base, struct function *fun)
3441debfc3dSmrg {
3451debfc3dSmrg   FILE *fp = open_graph_file (base, "a");
3461debfc3dSmrg   print_graph_cfg (fp, fun);
3471debfc3dSmrg   fclose (fp);
3481debfc3dSmrg }
3491debfc3dSmrg 
3501debfc3dSmrg /* Start the dump of a graph.  */
3511debfc3dSmrg static void
start_graph_dump(FILE * fp,const char * base)3521debfc3dSmrg start_graph_dump (FILE *fp, const char *base)
3531debfc3dSmrg {
3541debfc3dSmrg   pretty_printer graph_slim_pp;
3551debfc3dSmrg   graph_slim_pp.buffer->stream = fp;
3561debfc3dSmrg   pretty_printer *const pp = &graph_slim_pp;
3571debfc3dSmrg   pp_string (pp, "digraph \"");
3581debfc3dSmrg   pp_write_text_to_stream (pp);
3591debfc3dSmrg   pp_string (pp, base);
3601debfc3dSmrg   pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
3611debfc3dSmrg   pp_string (pp, "\" {\n");
3621debfc3dSmrg   pp_string (pp, "overlap=false;\n");
3631debfc3dSmrg   pp_flush (pp);
3641debfc3dSmrg }
3651debfc3dSmrg 
3661debfc3dSmrg /* End the dump of a graph.  */
3671debfc3dSmrg static void
end_graph_dump(FILE * fp)3681debfc3dSmrg end_graph_dump (FILE *fp)
3691debfc3dSmrg {
3701debfc3dSmrg   fputs ("}\n", fp);
3711debfc3dSmrg }
3721debfc3dSmrg 
3731debfc3dSmrg /* Similar as clean_dump_file, but this time for graph output files.  */
3741debfc3dSmrg void
clean_graph_dump_file(const char * base)3751debfc3dSmrg clean_graph_dump_file (const char *base)
3761debfc3dSmrg {
3771debfc3dSmrg   FILE *fp = open_graph_file (base, "w");
3781debfc3dSmrg   start_graph_dump (fp, base);
3791debfc3dSmrg   fclose (fp);
3801debfc3dSmrg }
3811debfc3dSmrg 
3821debfc3dSmrg 
3831debfc3dSmrg /* Do final work on the graph output file.  */
3841debfc3dSmrg void
finish_graph_dump_file(const char * base)3851debfc3dSmrg finish_graph_dump_file (const char *base)
3861debfc3dSmrg {
3871debfc3dSmrg   FILE *fp = open_graph_file (base, "a");
3881debfc3dSmrg   end_graph_dump (fp);
3891debfc3dSmrg   fclose (fp);
3901debfc3dSmrg }
391*8feb0f0bSmrg 
392*8feb0f0bSmrg #if __GNUC__ >= 10
393*8feb0f0bSmrg #  pragma GCC diagnostic pop
394*8feb0f0bSmrg #endif
395