diff mbox series

developer option: -fdump-generic-nodes; initial incorporation

Message ID 009401da65ae$8790a750$96b1f5f0$@symas.com
State New
Headers show
Series developer option: -fdump-generic-nodes; initial incorporation | expand

Commit Message

Robert Dubner Feb. 22, 2024, 4:45 p.m. UTC
As part of an effort to learn how create a GENERIC tree in order to
implement a
COBOL front end, I created the dump_generic_nodes(), which accepts a
function_decl at the point it is provided to the middle end.  The routine
generates three files.  One is ASCII, the second is HTML; they contain the
tree
in a human-readable form.  The third is JSON.

This commit modifies common.opt to accept the -fdump-generic-nodes
command-line
option, creates the dump-generic-nodes.cc and .h files to implement it,
and
inserts a call to the dump_generic_nodes() function near the top of
gimplify_function_tree() in gcc/gimplify.cc

This patch has been tested on X86_64-linux-gnu.  I haven't tried to
provide 
testcases for the automated system because 1) I haven't learned how to do
that,
and 2), I am not sure how to test this feature.  On the one hand, the
compiler
isn't affected when the switch isn't present; when it is present it seems
to
work on simple source code.

Legal requirements:  The FSF has on file an "employer disclaimer" for me.

I am using the "Signed off by" tag in an attempt to cover the legal bases;
I
trust I will be apprised of anything else that needs to be done.

gcc/ChangeLog:

	* developer options: -fdump-generic-nodes initial incorporation

Signed-off-by: Robert Dubner <rdubner@symas.com>
---
 gcc/Makefile.in           |    3 +-
 gcc/common.opt            |    4 +
 gcc/dump-generic-nodes.cc | 1958 +++++++++++++++++++++++++++++++++++++
 gcc/dump-generic-nodes.h  |   26 +
 gcc/gimplify.cc           |    3 +
 5 files changed, 1993 insertions(+), 1 deletion(-)
 create mode 100644 gcc/dump-generic-nodes.cc
 create mode 100644 gcc/dump-generic-nodes.h

Comments

Richard Biener Feb. 27, 2024, 9:11 a.m. UTC | #1
On Thu, Feb 22, 2024 at 5:46 PM Robert Dubner <rdubner@symas.com> wrote:
>
> As part of an effort to learn how create a GENERIC tree in order to
> implement a
> COBOL front end, I created the dump_generic_nodes(), which accepts a
> function_decl at the point it is provided to the middle end.  The routine
> generates three files.  One is ASCII, the second is HTML; they contain the
> tree
> in a human-readable form.  The third is JSON.
>
> This commit modifies common.opt to accept the -fdump-generic-nodes
> command-line
> option, creates the dump-generic-nodes.cc and .h files to implement it,
> and
> inserts a call to the dump_generic_nodes() function near the top of
> gimplify_function_tree() in gcc/gimplify.cc

While I think that's good and probably the best you can do in language
independent code GCCs 'GENERIC' is inherently frontend specific
(only GIMPLE is no longer).  The gimplifier you hook into eventually
relies on the 'gimplify_expr' language hook to deal with frontend specific
tree codes and there might be auxiliary info in the language-specific
portions of types and decls (DECL_LANG_SPECIFIC, TYPE_LANG_SPECIFIC).

That's a caveat only, it might mean that in some cases a
'dump_generic_to_json' language hook might be a nice thing to have.
Note this is likely only a "problem" for frontends not having their own
internal AST representation they lower to GENERIC;  first and foremost
the C and C++ language frontends - I'm not sure of others here, but
the presence of

./ada/gcc-interface/ada-tree.def
./c/c-tree.def
./cp/cp-tree.def
./d/d-tree.def
./m2/m2-tree.def
./objc/objc-tree.def

suggests that "issue" might be wide-spread.

> This patch has been tested on X86_64-linux-gnu.  I haven't tried to
> provide
> testcases for the automated system because 1) I haven't learned how to do
> that,
> and 2), I am not sure how to test this feature.  On the one hand, the
> compiler
> isn't affected when the switch isn't present; when it is present it seems
> to
> work on simple source code.

I think this is a useful feature.  I do wonder whether it makes more sense
to only implement JSON dumping inside the compiler and leave html
and text output to postprocessing that.  That avoids diverging information
detail and should reduce the amount of code to maintain.  Scripts to
perform analysis/transform of such JSON could be contributed into
the contrib/ directory.

To follow general filename standards for auxiliary files you should
prepend the filename(s) you dump to with 'aux_base_name', the
chance of accidentially trashing users files is then reduced a lot.

It might be that with JSON it's reasonable to output a single file per TU
only, avoiding issues with say C++ function overloading and possible
clashes on filenames.

https://gcc.gnu.org/contribute.html#standards lists some bits that
we expect to help common appearance of code and maintainance.
For example a lot of your functions have no function-level comment
documenting them.

> Legal requirements:  The FSF has on file an "employer disclaimer" for me.
>
> I am using the "Signed off by" tag in an attempt to cover the legal bases;
> I
> trust I will be apprised of anything else that needs to be done.

I think that's OK.

> gcc/ChangeLog:
>
>         * developer options: -fdump-generic-nodes initial incorporation
>
> Signed-off-by: Robert Dubner <rdubner@symas.com>
> ---
>  gcc/Makefile.in           |    3 +-
>  gcc/common.opt            |    4 +
>  gcc/dump-generic-nodes.cc | 1958 +++++++++++++++++++++++++++++++++++++
>  gcc/dump-generic-nodes.h  |   26 +
>  gcc/gimplify.cc           |    3 +
>  5 files changed, 1993 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/dump-generic-nodes.cc
>  create mode 100644 gcc/dump-generic-nodes.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index a74761b7ab3..81922b0884c 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1441,6 +1441,7 @@ OBJS = \
>         domwalk.o \
>         double-int.o \
>         dse.o \
> +       dump-generic-nodes.o \
>         dumpfile.o \
>         dwarf2asm.o \
>         dwarf2cfi.o \
> @@ -3857,7 +3858,7 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H)
> coretypes.h $(TM_H) \
>    hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h
> \
>    lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \
>    pass-instances.def params.list $(srcdir)/../include/gomp-constants.h \
> -  $(EXPR_H) $(srcdir)/analyzer/*.h
> +  $(EXPR_H) $(srcdir)/analyzer/*.h dump-generic-nodes.h
>
>  # generate the 'build fragment' b-header-vars
>  s-header-vars: Makefile
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 51c4a17da83..751b9b1f0cc 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1583,6 +1583,10 @@ fdump-passes
>  Common Var(flag_dump_passes) Init(0)
>  Dump optimization passes.
>
> +fdump-generic-nodes
> +Common Var(flag_dump_generic_nodes) Init(0)
> +Dump GENERIC trees for each function in three files: <funcname>.nodes,
> <funcname>.nodes.html, and <funcname>.json
> +
>  fdump-unnumbered
>  Common Var(flag_dump_unnumbered)
>  Suppress output of instruction numbers, line number notes and addresses
> in debugging dumps.
> diff --git a/gcc/dump-generic-nodes.cc b/gcc/dump-generic-nodes.cc
> new file mode 100644
> index 00000000000..d44119116d2
> --- /dev/null
> +++ b/gcc/dump-generic-nodes.cc
> @@ -0,0 +1,1958 @@
> +/* Prints out a tree of generic/gimple nodes in human readable form, both
> in
> +   straight text and in HTML. The entry point is dump_generic_nodes().
> +
> +   Copyright(C) 1990-2024 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or(at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tm.h"
> +#include "tree.h"
> +#include "cgraph.h"
> +#include "diagnostic.h"
> +#include "varasm.h"
> +#include "print-rtl.h"
> +#include "stor-layout.h"
> +#include "langhooks.h"
> +#include "tree-iterator.h"
> +#include "gimple-pretty-print.h"
> +#include "tree-cfg.h"
> +#include "dumpfile.h"
> +
> +#undef DEFTREESTRUCT
> +#define DEFTREESTRUCT(VAL, NAME) NAME,
> +static const char *ts_enum_names[] =
> +  {
> +  #include "treestruct.def"
> +  };
> +#undef DEFTREESTRUCT
> +
> +#define ADD_FLAG(accessor,text)if(accessor(node)){strcat(ach," " text);}
> +
> +static FILE *ftext = NULL;
> +static FILE *fhtml = NULL;
> +static FILE *fjson = NULL;
> +
> +static int json_level = 0;
> +static const char *json_comma;
> +static const int spaces_per_indent = 2;
> +
> +static void rjd_print_node(tree node);
> +
> +static int phase = 1;
> +
> +/* Define the hash table of nodes already seen.
> +   Such nodes are not repeated; brief cross-references are used.  */
> +
> +struct TREE
> +  {
> +  tree this_node;
> +  int     seen;
> +  } ;
> +
> +static struct TREE *nodes = NULL;
> +static int GV_size_of_tree = 0;
> +static int GV_number_of_nodes = 0;
> +
> +static void
> +json_fprintf(const char *format, ...) __attribute__ ((format (printf, 1,
> 2)));
> +
> +static void
> +json_fprintf(const char *format, ...)
> +{
> +  char ach1[2048];
> +  if( phase == 2 )
> +    {
> +    va_list args;
> +    va_start(args, format);
> +    vsnprintf(ach1, sizeof(ach1), format, args);
> +    va_end(args);
> +    fprintf(fjson, "%s", ach1);
> +    }
> +}
> +
> +static void
> +json_indent()
> +{
> +  for(int i=0; i<json_level*spaces_per_indent; i++)
> +    {
> +    json_fprintf(" ");
> +    }
> +}
> +
> +static void
> +json_newline()
> +{
> +  json_fprintf("%s\n", json_comma);
> +  json_indent();
> +}
> +
> +static int
> +find_node_in_nodes(tree node)
> +{
> +  // This is an O(n^2) abomination.  At a suitable time, replace it with
> a
> +  // hashed map!
> +  int retval = -1;
> +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> +  for(int i=0; i<GV_number_of_nodes; i++)
> +    {
> +    if( nodes[i].this_node == node )
> +      {
> +      retval = i;
> +      break;
> +      }
> +    }
> +  return retval;
> +}
> +
> +static int
> +add_node_to_nodes(tree node)
> +{
> +  // Assume we know the node isn't there yet:
> +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> +  if( GV_number_of_nodes == GV_size_of_tree )
> +    {
> +    // We've run out of room.  Double the number of nodes:
> +    GV_size_of_tree *= 2;
> +    if( GV_size_of_tree == 0 )
> +      {
> +      GV_size_of_tree = 64;
> +      }
> +    struct TREE *newnodes =
> +               (struct TREE *)xmalloc( GV_size_of_tree * sizeof(struct
> TREE) );
> +    memcpy(newnodes,nodes, GV_number_of_nodes * sizeof(struct TREE));
> +    free(nodes);
> +    nodes = newnodes;
> +    }
> +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> +  nodes[GV_number_of_nodes].this_node = node;
> +  nodes[GV_number_of_nodes].seen = 0;
> +  GV_number_of_nodes += 1;
> +  return GV_number_of_nodes-1;
> +}
> +
> +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
> +
> +static void
> +rjd_fprintf(const char *format, ...)
> +{
> +  // NOTE: I painfully learned that mixing fprintf() and vfprintf() calls
> +  // can result in hard-to-find memory allocation bugs.  Probably worth
> +  // tracking down, but the the problem shows up in the libc6 library
> +  // routines.
> +  char ach1[2048];
> +  char ach2[4196];
> +  char ach3[4196];
> +  if( phase == 2 )
> +    {
> +    va_list args;
> +    va_start(args, format);
> +    vsnprintf(ach1, sizeof(ach1), format, args);
> +    va_end(args);
> +
> +    fprintf(ftext, "%s", ach1);
> +
> +    // Copy ach1 to ach2, replacing '\n' with "<br>"
> +    char *p1 = ach1;
> +    char *p2 = ach2;
> +
> +    while( *p1 )
> +        {
> +        if(*p1 == '\n')
> +          {
> +          p1 += 1;
> +          *p2 = '\0';
> +          strcat(p2, "<br>");
> +          p2 = ach2 + strlen(ach2);
> +          }
> +        else
> +          {
> +          *p2++ = *p1++;
> +          }
> +        }
> +    size_t ach1_length = strlen(ach1);
> +
> +    // To make the HTML more-or-less readable, if ach1 ends with a
> newline,
> +    // make ach2 end with a newline as well.
> +    if( ach1_length && ach1[ach1_length-1] == '\n' )
> +      {
> +      *p2++ = '\n';
> +      }
> +    *p2++ = '\0';
> +
> +    // We now look for NodeNumberNNN and replace it with
> +    // <a href="#NodeNumberNNN">NodeNumberNNN</a>
> +
> +    char *pleft = strstr(ach2, "NodeNumber");
> +    if( pleft )
> +      {
> +      char *pright = pleft + strlen("NodeNumber");
> +      while( *pright >= '0' && *pright <= '9' )
> +        {
> +        pright += 1;
> +        }
> +      memset(ach3, 0, sizeof(ach3));
> +      memcpy(ach3, ach2, pleft - ach2);
> +      strcat(ach3, "<a href=\"#" );
> +      char *p = ach3 + strlen(ach3);
> +      memcpy(p, pleft, pright-pleft);
> +      strcat(ach3,"\">");
> +      p = ach3 + strlen(ach3);
> +      memcpy(p, pleft, pright-pleft);
> +      p = ach3 + strlen(ach3);
> +      strcat(ach3,"</a>");
> +      p = ach3 + strlen(ach3);
> +      strcpy(p, pright);
> +      strcpy(ach2, ach3);
> +      }
> +
> +    fprintf(fhtml, "%s", ach2);
> +    }
> +}
> +
> +#define NOT_QUOTED false
> +static void
> +json_namevalue(const char *name, const char *value, bool quoted = true)
> +{
> +  if( phase == 2 )
> +    {
> +    json_newline();
> +    if( quoted )
> +      {
> +      json_fprintf("\"%s\":\"%s\"", name, value);
> +      }
> +    else
> +      {
> +      json_fprintf("\"%s\":%s", name, value);
> +      }
> +    }
> +}
> +
> +static void
> +html_boilerplate(char *title=NULL)
> +{
> +  if( fhtml )
> +    {
> +    if( title )
> +      {
> +      fprintf(fhtml,
> +              "<!DOCTYPE html>\n"
> +              "<html lang=\"en\">\n"
> +              "  <head>\n"
> +              "    <meta charset=\"utf-8\">\n"
> +              "    <title>%s</title>\n"
> +              "    <style>\n"
> +              "    body\n"
> +              "        {\n"
> +              "        font-family: courier, serif;\n"
> +              "        font-size: 13px;\n"
> +              "        }\n"
> +              "    </style>\n"
> +              "  </head>\n"
> +              "  <body>\n", title);
> +      }
> +    else
> +      {
> +      fprintf(fhtml, "%s",
> +              "  </body>\n"
> +              "</html>\n");
> +      }
> +    }
> +}
> +
> +/* Print a node in brief fashion*/
> +
> +static void
> +print_name(const char *prefix, tree node)
> +{
> +  if( node && TREE_CODE_CLASS(TREE_CODE(node)) == tcc_declaration )
> +    {
> +    tree name = DECL_NAME(node);
> +    if( name )
> +      {
> +      rjd_fprintf("%s\"%s\"", prefix, IDENTIFIER_POINTER(name));
> +      }
> +    }
> +}
> +
> +static void
> +rjd_subtree(const char *subtree_name, tree subtree)
> +{
> +  if( subtree )
> +    {
> +    if( phase == 1 )
> +      {
> +      // Make sure the subtree is in the list of nodes.  And, yes, this
> +      // is a potential headachy recursivy thingummy.  But computers are
> +      // good at that.
> +      rjd_print_node(subtree);
> +
> +      int subtree_node_number = find_node_in_nodes(subtree);
> +      if( subtree_node_number == -1 )
> +        {
> +        printf("Run in circles, scream and shout!\n");
> +        exit(1);
> +        }
> +
> +      }
> +    else if( phase == 2 )
> +      {
> +      char ach[512];
> +      int node_number = find_node_in_nodes(subtree);
> +      if( node_number >= 0 )
> +        {
> +        rjd_fprintf("%s: NodeNumber%d", subtree_name, node_number);
> +
> +        // Handle a few subthings that are very common, and useful to
> see:
> +        gcc_assert(node_number < GV_number_of_nodes
> +                   && GV_number_of_nodes <= GV_size_of_tree);
> +        tree subnode = nodes[node_number].this_node;
> +        if( subnode )
> +          {
> +          enum tree_code subcode = TREE_CODE(subnode);
> +
> +          // After the NodeNumber, print the code name.  Unless it is an
> +          // integer_cst, because we convert that to int32 or uint64
> later.
> +          if( subcode != INTEGER_CST )
> +            {
> +            rjd_fprintf(" %s", get_tree_code_name(subcode));
> +            }
> +
> +          if( subcode == IDENTIFIER_NODE && IDENTIFIER_POINTER(subnode) )
> +            {
> +            rjd_fprintf(" \"%s\"", IDENTIFIER_POINTER(subnode));
> +            }
> +
> +          print_name(" ", subnode);
> +
> +          if( subcode == INTEGER_CST )
> +            {
> +            tree int_cst_type = TREE_TYPE(subnode);
> +            tree_code int_cst_type_code = TREE_CODE(int_cst_type);
> +
> +            if( int_cst_type_code == POINTER_TYPE )
> +              {
> +              rjd_fprintf(" pointer");
> +              }
> +            else if( int_cst_type_code == INTEGER_TYPE)
> +              {
> +              // The int_cst_type is an integer_type, so...
> +              tree min_value = TYPE_MIN_VALUE_RAW(int_cst_type);
> +              print_dec(wi::to_wide(min_value),
> +                        ach,
> +                        TYPE_SIGN(TREE_TYPE(min_value)));
> +              if( strcmp( ach, "0") )
> +                {
> +                rjd_fprintf(" int",ach);
> +                }
> +              else
> +                {
> +                rjd_fprintf(" uint",ach);
> +                }
> +
> +              tree size_in_bits = TYPE_SIZE(int_cst_type);
> +
> +              print_dec(wi::to_wide(size_in_bits),
> +                        ach,
> +                        TYPE_SIGN(TREE_TYPE(size_in_bits)));
> +              rjd_fprintf(ach);
> +
> +              print_dec(wi::to_wide(subnode),
> +                        ach, TYPE_SIGN(TREE_TYPE(subnode)));
> +              }
> +
> +            print_dec(wi::to_wide(subnode),
> +                      ach,
> +                      TYPE_SIGN(TREE_TYPE(subnode)));
> +            rjd_fprintf(" %s",ach);
> +            }
> +
> +          if( subcode == DECL_EXPR )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len )
> +              {
> +              print_name(" ", TREE_OPERAND(subnode, 0));
> +              }
> +            }
> +
> +          if( subcode == CALL_EXPR )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len > 1)
> +              {
> +              tree addr_expression = TREE_OPERAND(subnode, 1);
> +              int len2 = TREE_OPERAND_LENGTH(addr_expression);
> +              if( len2 )
> +                {
> +                print_name(" ", TREE_OPERAND(addr_expression, 0));
> +                }
> +              }
> +            }
> +
> +          if( subcode == ADDR_EXPR  )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len > 0)
> +              {
> +              print_name(" ", TREE_OPERAND(subnode, 0));
> +              }
> +            }
> +
> +          if( subcode == MODIFY_EXPR )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len > 0)
> +              {
> +              tree target = TREE_OPERAND(subnode, 0);
> +              tree_code target_code = TREE_CODE(target);
> +              tree_code_class target_code_class =
> TREE_CODE_CLASS(target_code);
> +              if( target_code_class == tcc_declaration )
> +                {
> +                print_name(" ", target);
> +                }
> +              else if( target_code == COMPONENT_REF
> +                                      && target_code_class ==
> tcc_reference )
> +                {
> +                int len = TREE_OPERAND_LENGTH(target);
> +                tree structure = NULL_TREE;
> +                tree field     = NULL_TREE;
> +                if( len > 0 )
> +                  {
> +                  structure = TREE_OPERAND(target, 0);
> +                  }
> +                if( len > 1 )
> +                  {
> +                  field = TREE_OPERAND(target, 1);
> +                  }
> +                print_name(" ", structure);
> +                print_name("::", field);
> +                }
> +              }
> +            }
> +
> +          rjd_fprintf("\n");
> +          }
> +        // In contrast, for the JSON output, we just want the node
> number:
> +        // But we need to intervene in the case where the subtree name
> starts
> +        // off with "operand[nnn]".  We are putting them in JSON arraysm,
> so we
> +        // need to eliminate that name
> +        if( strstr(subtree_name, "constructor_elt[") != subtree_name )
> +          {
> +          // constructors are handled separately because they have both
> index
> +          // and value members.  Search for "constructor_elt" to find
> that code.
> +          if(    strstr(subtree_name, "operand[") == subtree_name
> +              || strstr(subtree_name, "tree_vector_element[") ==
> subtree_name
> +              || strstr(subtree_name, "statement_list[") == subtree_name
> +              || strstr(subtree_name, "nonlocalized_var[") ==
> subtree_name
> +              )
> +            {
> +            // We are elminating the subtree name
> +            json_newline();
> +            }
> +          else
> +            {
> +            json_namevalue(subtree_name, "", NOT_QUOTED);
> +            }
> +          json_fprintf("{\"node\":%d}", node_number);
> +          }
> +        }
> +      else
> +        {
> +        // This can happen as a result of certain compilation
> +        // errors.   Just ignore them.
> +        }
> +      }
> +    }
> +  else
> +    {
> +    if( strstr(subtree_name, "operand[") == subtree_name )
> +      {
> +      json_newline();
> +      json_fprintf("{\"node\":null}");
> +      }
> +
> +    }
> +
> +}
> +
> +static void
> +json_flags(const char *name, const char *ach)
> +{
> +  const char *p = ach+1;  // Skip past the initial space
> +  json_namevalue(name, "",  NOT_QUOTED);
> +  json_level += 1;
> +  json_comma = "";
> +  json_fprintf("\n");
> +  json_indent();
> +  json_fprintf("{");
> +
> +  while( *p )
> +    {
> +    const char *pend = strchr(p, ' ');
> +    if( !pend )
> +      {
> +      pend = p + strlen(p);
> +      }
> +    char achName[256];
> +    char *d = achName;
> +    while(p < pend )
> +      {
> +      *d++ = *p++;
> +      }
> +    *d++= '\0';
> +    if( *p == ' ' )
> +      {
> +      p += 1;
> +      }
> +    json_namevalue(achName, "true", NOT_QUOTED);
> +    json_comma = ",";
> +    }
> +  json_fprintf("\n");
> +  json_indent();
> +  json_fprintf("}");
> +
> +  json_level -= 1;
> +  json_comma = ",";
> +}
> +
> +static void
> +json_start_array(const char *name)
> +{
> +  // We will set up a JSON array of operands
> +  json_namevalue(name,"", NOT_QUOTED);
> +  json_fprintf("\n");
> +  json_level += 1;
> +  json_indent();
> +  json_fprintf("[");
> +  json_comma = "";
> +}
> +static void
> +json_finish_array()
> +{
> +  json_fprintf("\n");
> +  json_indent();
> +  json_fprintf("]");
> +  json_level -= 1;
> +  json_comma = ",";
> +}
> +
> +static void
> +rjd_print_node(tree node)
> +{
> +  char ach[4096];
> +  enum tree_code_class tclass;
> +  int len;
> +  int i;
> +  expanded_location xloc;
> +  enum tree_code code;
> +
> +  if(node == 0)
> +    {
> +    // When handled a NULL, just return
> +    return;
> +    }
> +
> +  int node_number = find_node_in_nodes(node);
> +  if( phase == 1 )
> +    {
> +    if( node_number != -1 )
> +      {
> +      // We're building the list of nodes, and we've already processed
> this
> +      // node:
> +      return;
> +      }
> +
> +    // We are building the list of nodes, and this is a new one:
> +    node_number = add_node_to_nodes(node);
> +    }
> +  // From here on out, we know that node_number is valid, whether in
> +  // phase 1 or phase 2
> +
> +  code = TREE_CODE(node);
> +
> +  /* It is unsafe to look at any other fields of a node with ERROR_MARK
> or
> +     invalid code.  */
> +  if(code == ERROR_MARK || code >= MAX_TREE_CODES)
> +    {
> +    rjd_fprintf("This node is unsafe.  The reported TREE_CODE is
> %d\n",code);
> +    return;
> +    }
> +
> +  tclass = TREE_CODE_CLASS(code);
> +
> +  /* Announce the coming of a new node: */
> +  if( phase==2 && fhtml )
> +    {
> +    fprintf(fhtml, "<p id=\"NodeNumber%d\">\n", node_number);
> +    }
> +  rjd_fprintf("***********************************This is
> NodeNumber%d\n",
> +              node_number);
> +
> +  /* Print the NodeNumber in a more canonical form: */
> +  rjd_fprintf("(%p) NodeNumber%d\n", node, node_number);
> +
> +  // Print the tree_code for this node
> +  rjd_fprintf("tree_code: %s\n", get_tree_code_name(code));
> +  json_namevalue("tree_code", get_tree_code_name(code));
> +
> +  // It might be useful to see the class
> +  const char *classtxt;
> +  switch(tclass)
> +    {
> +    case tcc_exceptional:
> +      classtxt = "tcc_exceptional";
> +      break;
> +    case tcc_constant:
> +      classtxt = "tcc_constant";
> +      break;
> +    case tcc_type:
> +      classtxt = "tcc_type";
> +      break;
> +    case tcc_declaration:
> +      classtxt = "tcc_declaration";
> +      break;
> +    case tcc_reference:
> +      classtxt = "tcc_reference";
> +      break;
> +    case tcc_comparison:
> +      classtxt = "tcc_comparison";
> +      break;
> +    case tcc_unary:
> +      classtxt = "tcc_unary";
> +      break;
> +    case tcc_binary:
> +      classtxt = "tcc_binary";
> +      break;
> +    case tcc_statement:
> +      classtxt = "tcc_statement";
> +      break;
> +    case tcc_vl_exp:
> +      classtxt = "tcc_vl_exp";
> +      break;
> +    case tcc_expression:
> +      classtxt = "tcc_expression";
> +      break;
> +    default:
> +      gcc_unreachable();
> +      break;
> +    }
> +  rjd_fprintf("tree_code_class: %s\n", classtxt);
> +  json_namevalue("tree_code_class", classtxt);
> +
> +  int required[64];
> +  int processed[64];
> +  for(int i=0; i<64; i++)
> +    {
> +    required[i] = CODE_CONTAINS_STRUCT(code, i);
> +    processed[i] = 0;
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_BASE) )
> +    {
> +    processed[TS_BASE] = 1;
> +    // There are 16 bits in TS_BASE.  The trouble is, they have
> +    // different meanings for different codes; see the extensive
> +    // comment in tree-core.h
> +    strcpy(ach, "");
> +    if( tclass != tcc_type && TREE_SIDE_EFFECTS(node) )
> +      {
> +      strcat(ach," side_effects");
> +      }
> +    if( tclass != tcc_type && TREE_CONSTANT(node) )
> +      {
> +      strcat(ach," constant");
> +      }
> +    if( TREE_ADDRESSABLE(node) )
> +      {
> +      strcat(ach," addressable");
> +      }
> +    if( TREE_THIS_VOLATILE(node) )
> +      {
> +      strcat(ach," volatile");
> +      }
> +    if( tclass != tcc_type && TREE_READONLY(node) )
> +      {
> +      strcat(ach," readonly");
> +      }
> +    if( TREE_ASM_WRITTEN(node) )
> +      {
> +      strcat(ach," asm_written");
> +      }
> +    if( TREE_NO_WARNING(node) )
> +      {
> +      strcat(ach," nowarning");
> +      }
> +    if( TREE_VISITED(node) )
> +      {
> +      strcat(ach," visited");
> +      }
> +
> +    if( TREE_USED(node) )
> +      {
> +      strcat(ach," used");
> +      }
> +    if( TREE_NOTHROW(node) )
> +      {
> +      strcat(ach," nothrow");
> +      }
> +    if( TREE_STATIC(node) )
> +      {
> +      strcat(ach," static");
> +      }
> +    if( TREE_PUBLIC(node) )
> +      {
> +      strcat(ach," public");
> +      }
> +    if( TREE_PRIVATE(node) )
> +      {
> +      strcat(ach," private");
> +      }
> +    if( TREE_PROTECTED(node) )
> +      {
> +      strcat(ach," protected");
> +      }
> +    if( TREE_DEPRECATED(node) )
> +      {
> +      strcat(ach," deprecated");
> +      }
> +    if( node->base.default_def_flag ) //There isn't a TREE_DEFAULT_DEF
> macro
> +      {
> +      strcat(ach," default_def");
> +      }
> +    if( tclass == tcc_constant )
> +      {
> +      if( TREE_OVERFLOW(node) )
> +        {
> +        strcat(ach," overflow");
> +        }
> +      }
> +    if( tclass == tcc_type )
> +      {
> +      ADD_FLAG(TYPE_UNSIGNED,   "unsigned");
> +      ADD_FLAG(TYPE_PACKED,     "packed");
> +      ADD_FLAG(TYPE_USER_ALIGN, "user_align");
> +      ADD_FLAG(TYPE_NAMELESS,   "nameless");
> +      ADD_FLAG(TYPE_ATOMIC,     "atomic");
> +      }
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("base_flags:%s\n", ach);
> +      json_flags("base_flags", ach);
> +      }
> +    }
> +
> +  if( tclass == tcc_type )
> +    {
> +    sprintf(ach, "%s(%u)",
> +            GET_MODE_NAME(TYPE_MODE(node)),(int)TYPE_MODE(node));
> +    rjd_fprintf("machine_mode: %s\n", ach);
> +    json_namevalue("machine_mode", ach);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPED) )
> +    {
> +    processed[TS_TYPED] = 1;
> +    rjd_subtree("type", TREE_TYPE(node));
> +    if( tclass == tcc_type )
> +      {
> +      sprintf(ach, "%u", (int)TYPE_ADDR_SPACE(node));
> +      rjd_fprintf("address_space:%s\n", ach);
> +      json_namevalue("address_space", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_MINIMAL) )
> +    {
> +    processed[TS_DECL_MINIMAL] = 1;
> +    rjd_subtree("name", DECL_NAME(node));
> +    rjd_subtree("context", DECL_CONTEXT(node));
> +    xloc = expand_location(DECL_SOURCE_LOCATION(node));
> +    if( xloc.file )
> +      {
> +      sprintf(ach, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
> +      rjd_fprintf("source_location: %s\n", ach);
> +      json_namevalue("source_location", ach);
> +      }
> +    sprintf(ach, "%d", DECL_PT_UID(node));
> +    rjd_fprintf("uid: %s\n", ach);
> +    json_namevalue("uid", ach, NOT_QUOTED);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_COMMON) )
> +    {
> +    processed[TS_DECL_COMMON] = 1;
> +    rjd_subtree("size(in bits)", DECL_SIZE(node));
> +    rjd_subtree("size_unit(in bytes)", DECL_SIZE_UNIT(node));
> +
> +    const char *p;
> +    switch(code)
> +      {
> +      case FUNCTION_DECL:
> +        p = "initial(bindings)";
> +        break;
> +      case TRANSLATION_UNIT_DECL:
> +        p = "initial(block)";
> +        break;
> +      case VAR_DECL:
> +        p = "initial value";
> +        break;
> +      default:
> +        p = "initial";
> +        break;
> +      }
> +    rjd_subtree(p, DECL_INITIAL(node));
> +    rjd_subtree("attributes", DECL_ATTRIBUTES(node));
> +    rjd_subtree("abstract_origin", DECL_ABSTRACT_ORIGIN(node));
> +
> +    sprintf(ach, "%s(%u)",
> GET_MODE_NAME(DECL_MODE(node)),(int)DECL_MODE(node));
> +    rjd_fprintf("machine_mode: %s\n", ach);
> +    json_namevalue("machine_mode", ach);
> +
> +    strcpy(ach, "");
> +    if( DECL_NONLOCAL(node) )
> +      {
> +      strcat(ach," nonlocal");
> +      }
> +    if( DECL_VIRTUAL_P(node) )
> +      {
> +      strcat(ach," virtual");
> +      }
> +    if( DECL_IGNORED_P(node) )
> +      {
> +      strcat(ach," ignored");
> +      }
> +    if( DECL_ABSTRACT_P(node) )
> +      {
> +      strcat(ach," abstrac");
> +      }
> +    if( DECL_ARTIFICIAL(node) )
> +      {
> +      strcat(ach," artificial");
> +      }
> +    if( DECL_PRESERVE_P(node) )
> +      {
> +      strcat(ach," preserve");
> +      }
> +    if( code == VAR_DECL && DECL_DEBUG_EXPR(node) )
> +      {
> +      strcat(ach," debug_expr_is_from");
> +      }
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("decl_flags:%s\n",ach);
> +      json_flags("decl_flags", ach);
> +      }
> +
> +    if( code == FIELD_DECL )
> +      {
> +      sprintf(ach, "%u", (int)DECL_OFFSET_ALIGN(node));
> +      rjd_fprintf("offset_align: %s\n", ach);
> +      json_namevalue("offset_align", ach);
> +      }
> +    sprintf(ach, "%u", (int)DECL_ALIGN(node));
> +    rjd_fprintf("align: %u\n", ach);
> +    json_namevalue("align", ach, NOT_QUOTED);
> +
> +    sprintf(ach, "%u", (int)DECL_WARN_IF_NOT_ALIGN(node));
> +    rjd_fprintf("warn_if_not_align: %s\n", ach);
> +    json_namevalue("warn_if_not_align", ach, NOT_QUOTED);
> +
> +    sprintf(ach, "%u", (int)DECL_PT_UID(node));
> +    rjd_fprintf("pt_uid: %s\n", ach);
> +    json_namevalue("pt_uid", ach, NOT_QUOTED);
> +
> +    if( DECL_LANG_SPECIFIC(node) )
> +      {
> +      rjd_fprintf("lang_specific(pointer):
> %p\n",DECL_LANG_SPECIFIC(node));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WRTL) )
> +    {
> +    processed[TS_DECL_WRTL] = 1;
> +    if( DECL_RTL_SET_P(node) )
> +      {
> +      rjd_fprintf("DECL_RTL for NODE has already been set\n");
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WITH_VIS) )
> +    {
> +    processed[TS_DECL_WITH_VIS] = 1;
> +    rjd_subtree("raw_assembler_name", DECL_ASSEMBLER_NAME_RAW(node));
> +    if( DECL_LANG_SPECIFIC(node) )
> +      {
> +      rjd_fprintf("symtab_node(pointer): %p\n",DECL_LANG_SPECIFIC(node));
> +      }
> +    strcpy(ach, "");
> +    if( code == VAR_DECL )
> +      {
> +      if( DECL_DEFER_OUTPUT(node) )
> +        {
> +        strcat(ach, " defer_output");
> +        }
> +      if( DECL_HARD_REGISTER(node) )
> +        {
> +        strcat(ach, " hard_register");
> +        }
> +      if( DECL_COMMON(node) )
> +        {
> +        strcat(ach, " common");
> +        }
> +      if( DECL_IN_TEXT_SECTION(node) )
> +        {
> +        strcat(ach, " in_text_section");
> +        }
> +      if( DECL_IN_CONSTANT_POOL(node) )
> +        {
> +        strcat(ach, " in_constant_pool");
> +        }
> +      if( DECL_DLLIMPORT_P(node) )
> +        {
> +        strcat(ach, " dllimport_flag");
> +        }
> +      }
> +    if( DECL_WEAK(node) )
> +      {
> +      strcat(ach, " weak_flag");
> +      }
> +    if( DECL_SEEN_IN_BIND_EXPR_P(node) )
> +      {
> +      strcat(ach, " seen_in_bind_expr");
> +      }
> +    if( DECL_COMDAT(node) )
> +      {
> +      strcat(ach, " comdat_flag");
> +      }
> +    if( DECL_VISIBILITY_SPECIFIED(node) )
> +      {
> +      strcat(ach," visibility_specified");
> +      }
> +    if( code == VAR_DECL && DECL_HAS_INIT_PRIORITY_P(node) )
> +      {
> +      strcat(ach," init_priority_p");
> +      }
> +    if(  code == FUNCTION_DECL && DECL_FINAL_P(node) )
> +      {
> +      strcat(ach," final");
> +      }
> +    if(  code == FUNCTION_DECL && DECL_STATIC_CHAIN(node) )
> +      {
> +      strcat(ach," regdecl_flag");
> +      }
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("decl_with_vis flags:%s\n",ach);
> +      json_flags("decl_with_vis", ach);
> +      }
> +
> +    const char *p = "???";
> +    switch( DECL_VISIBILITY(node) )
> +      {
> +      case VISIBILITY_DEFAULT:
> +        p = "default";
> +        break;
> +      case VISIBILITY_PROTECTED:
> +        p = "protected";
> +        break;
> +      case VISIBILITY_HIDDEN:
> +        p = "hidden";
> +        break;
> +      case VISIBILITY_INTERNAL:
> +        p = "internal";
> +        break;
> +      }
> +    rjd_fprintf("visibility: %s\n", p);
> +    json_namevalue("visibility", p);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_NON_COMMON) )
> +    {
> +    processed[TS_DECL_NON_COMMON] = 1;
> +    rjd_subtree("result", DECL_RESULT_FLD(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_FUNCTION_DECL) )
> +    {
> +    processed[TS_FUNCTION_DECL] = 1;
> +    if( node->function_decl.f )
> +      {
> +      rjd_fprintf("function(pointer): %p\n",node->function_decl.f);
> +      }
> +
> +    rjd_subtree("arguments", DECL_ARGUMENTS(node));
> +    rjd_subtree("personality", DECL_FUNCTION_PERSONALITY(node));
> +    rjd_subtree("function_specific_target",
> +                              DECL_FUNCTION_SPECIFIC_TARGET(node));
> +    rjd_subtree("function_specific_optimization",
> +                              DECL_FUNCTION_SPECIFIC_OPTIMIZATION(node));
> +    const char *p = "saved_tree";
> +    if( code == FUNCTION_DECL )
> +      {
> +      p = "saved_tree(function_body)";
> +      }
> +    rjd_subtree(p, DECL_SAVED_TREE(node));
> +    rjd_subtree("vindex", DECL_VINDEX(node));
> +
> +    rjd_fprintf("function_code: %d\n",node->function_decl.function_code);
> +
> +    switch(DECL_BUILT_IN_CLASS(node))
> +      {
> +      case NOT_BUILT_IN:
> +        break;
> +      case BUILT_IN_FRONTEND:
> +        rjd_fprintf("built_in: frontend\n");
> +        json_namevalue("built_in", "frontend");
> +        break;
> +      case BUILT_IN_MD:
> +        rjd_fprintf("built_in: md\n");
> +        json_namevalue("built_in", "md");
> +        break;
> +      case BUILT_IN_NORMAL:
> +        rjd_fprintf("built_in: normal\n");
> +        json_namevalue("built_in", "normal");
> +        break;
> +      }
> +    switch(FUNCTION_DECL_DECL_TYPE(node))
> +      {
> +      case NONE:
> +        break;
> +      case OPERATOR_NEW:
> +        rjd_fprintf("operator_new: 1\n");
> +        json_namevalue("operator_new", "1", NOT_QUOTED);
> +        break;
> +      case OPERATOR_DELETE:
> +        rjd_fprintf("operator_delete: 1\n");
> +        json_namevalue("operator_delete", "1", NOT_QUOTED);
> +        break;
> +      case LAMBDA_FUNCTION:
> +        rjd_fprintf("lambda_function: 1\n");
> +        json_namevalue("lambda_function", "1", NOT_QUOTED);
> +        break;
> +      }
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_PUBLIC, "public");
> +    ADD_FLAG(DECL_STATIC_CONSTRUCTOR, "static_ctor_flag");
> +    ADD_FLAG(DECL_STATIC_DESTRUCTOR, "static_dtor_flag");
> +    ADD_FLAG(DECL_UNINLINABLE, "uninlinable");
> +    ADD_FLAG(DECL_POSSIBLY_INLINED, "possibly_inlined");
> +    ADD_FLAG(DECL_IS_NOVOPS, "novops_flag");
> +    ADD_FLAG(DECL_IS_RETURNS_TWICE, "returns_twice_flag");
> +    ADD_FLAG(DECL_IS_MALLOC, "malloc_flag");
> +    ADD_FLAG(DECL_DECLARED_INLINE_P, "declared_inline_flag");
> +    ADD_FLAG(DECL_NO_INLINE_WARNING_P, "no_inline_warning_flag");
> +    ADD_FLAG(DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT,
> +             "no_instrument_function_entry_exit");
> +    ADD_FLAG(DECL_NO_LIMIT_STACK, "no_limit_stack");
> +    if( TREE_CODE(node) == FUNCTION_DECL )
> +      {
> +      ADD_FLAG(DECL_DISREGARD_INLINE_LIMITS, "disregard_inline_limits");
> +      }
> +    ADD_FLAG(DECL_PURE_P, "pure_flag");
> +    ADD_FLAG(DECL_LOOPING_CONST_OR_PURE_P, "looping_const_or_pure_flag");
> +    ADD_FLAG(DECL_HAS_DEBUG_ARGS_P, "has_debug_args_flag");
> +    ADD_FLAG(DECL_FUNCTION_VERSIONED, "versioned_function");
> +    ADD_FLAG(DECL_IS_REPLACEABLE_OPERATOR, "replaceable_operator");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("function_flags:%s\n", ach);
> +      json_flags("function_flags", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_COMMON) )
> +    {
> +    processed[TS_TYPE_COMMON] = 1;
> +
> +    rjd_subtree("size(in bits)", TYPE_SIZE(node));
> +    rjd_subtree("size_unit(in bytes)", TYPE_SIZE_UNIT(node));
> +    rjd_subtree("attributes", TYPE_ATTRIBUTES(node));
> +
> +    sprintf(ach, "%d", TYPE_UID(node));
> +    rjd_fprintf("uid: %s\n", ach);
> +    json_namevalue("uid", ach, NOT_QUOTED);
> +
> +    if( !VECTOR_TYPE_P(node) )
> +      {
> +      sprintf(ach, "%d", TYPE_PRECISION(node));
> +      rjd_fprintf("precision: %s\n", ach);
> +      json_namevalue("precision", ach, NOT_QUOTED);
> +      }
> +
> +    sprintf(ach, "%d", TYPE_CONTAINS_PLACEHOLDER_INTERNAL(node));
> +    rjd_fprintf("contains_placeholder: %d\n", ach);
> +    json_namevalue("contains_placeholder", ach, NOT_QUOTED);
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(TYPE_NO_FORCE_BLK,"no_force_blk_flag");
> +    ADD_FLAG(TYPE_NEEDS_CONSTRUCTING,"needs_constructing_flag");
> +    if( code == RECORD_TYPE
> +        || code == UNION_TYPE
> +        || code == QUAL_UNION_TYPE )
> +      {
> +      ADD_FLAG(TYPE_TRANSPARENT_AGGR,"transparent_aggr_flag");
> +      }
> +    ADD_FLAG(TYPE_RESTRICT, "restrict_flag");
> +    if( code == RECORD_TYPE
> +        || code == UNION_TYPE
> +        || code == QUAL_UNION_TYPE
> +        || code == ARRAY_TYPE )
> +      {
> +      ADD_FLAG(TYPE_TYPELESS_STORAGE, "typeless_storage");
> +      }
> +    ADD_FLAG(TYPE_EMPTY_P, "empty_flag");
> +    ADD_FLAG(TYPE_INDIVISIBLE_P, "indivisible_p");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("type_common_flags:%s\n", ach);
> +      json_flags("type_common_flags", ach);
> +      }
> +
> +    sprintf(ach, "%d",  TYPE_ALIGN(node));
> +    rjd_fprintf("align: %s\n", ach);
> +    json_namevalue("align", ach);
> +
> +    sprintf(ach, "%d", TYPE_WARN_IF_NOT_ALIGN(node));
> +    rjd_fprintf("warn_if_not_align: %s\n", ach);
> +    json_namevalue("warn_if_not_align", ach);
> +
> +    sprintf(ach, "%d", TYPE_ALIAS_SET(node));
> +    rjd_fprintf("alias_set_type: %s\n", ach);
> +    json_namevalue("alias_set_type", ach);
> +
> +    rjd_subtree("pointer_to", TYPE_POINTER_TO(node));
> +    rjd_subtree("reference_to", TYPE_REFERENCE_TO(node));
> +
> +    rjd_subtree("canonical", TYPE_CANONICAL(node));
> +    rjd_subtree("next_variant", TYPE_NEXT_VARIANT(node));
> +    rjd_subtree("main_variant", TYPE_MAIN_VARIANT(node));
> +    rjd_subtree("context", TYPE_CONTEXT(node));
> +    rjd_subtree("name", TYPE_REFERENCE_TO(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_NON_COMMON) )
> +    {
> +    processed[TS_TYPE_NON_COMMON] = 1;
> +    rjd_subtree("values", TYPE_VALUES_RAW(node));
> +    rjd_subtree("minval", TYPE_MIN_VALUE_RAW(node));
> +    rjd_subtree("maxval", TYPE_MAX_VALUE_RAW(node));
> +    rjd_subtree("lang_1", TYPE_LANG_SLOT_1(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_WITH_LANG_SPECIFIC) )
> +    {
> +    processed[TS_TYPE_WITH_LANG_SPECIFIC] = 1;
> +    if( TYPE_LANG_SPECIFIC(node) )
> +      {
> +      rjd_fprintf("lang_type(pointer): %p\n",TYPE_LANG_SPECIFIC(node));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_INT_CST) )
> +    {
> +    processed[TS_INT_CST] = 1;
> +    if( phase == 2 )
> +    rjd_fprintf("value: ");
> +    print_dec(wi::to_wide(node), ach, TYPE_SIGN(TREE_TYPE(node)));
> +    rjd_fprintf("%s" , ach);
> +    rjd_fprintf("\n");
> +    json_namevalue("value", ach, NOT_QUOTED);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_REAL_CST) )
> +    {
> +    bool not_quoted = false;
> +    processed[TS_REAL_CST] = 1;
> +
> +    if(TREE_OVERFLOW(node))
> +      {
> +      strcpy(ach, " overflow ");
> +      }
> +    else
> +      {
> +      REAL_VALUE_TYPE d = TREE_REAL_CST(node);
> +      if(REAL_VALUE_ISINF(d))
> +        {
> +        strcpy(ach, REAL_VALUE_NEGATIVE(d) ? " -Inf" : " Inf");
> +        }
> +      else if(REAL_VALUE_ISNAN(d))
> +        {
> +        /* Print a NaN in the format [-][Q]NaN[(significand[exponent])]
> +         where significand is a hexadecimal string that starts with
> +         the 0x prefix followed by 0 if the number is not canonical
> +         and a non-zero digit if it is, and exponent is decimal.  */
> +        sprintf(ach,
> +                "%s%sNaN",
> +                d.sign ? "-" : "",
> +                d.signalling ? "S" : "Q");
> +        }
> +      else
> +        {
> +        real_to_decimal(ach, &d, sizeof(ach), 0, 1);
> +        not_quoted = true;
> +        }
> +      }
> +
> +    rjd_fprintf("value: %s\n", ach);
> +    json_namevalue("value", ach, not_quoted);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_VEC) )
> +    {
> +    processed[TS_VEC] = 1;
> +
> +    len = TREE_VEC_LENGTH(node);
> +    if(len)
> +      {
> +      json_start_array("tree_vector_elements");
> +      }
> +    for(i = 0; i < len; i++)
> +      {
> +      if( TREE_VEC_ELT(node, i)  )
> +        {
> +        char temp[32];
> +        sprintf(temp, "tree_vector_element[%d]", i);
> +
> +        rjd_subtree(temp, TREE_VEC_ELT(node, i));
> +        json_comma = ",";
> +        }
> +      }
> +    if(len)
> +      {
> +      json_finish_array();
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_EXP) )
> +    {
> +    processed[TS_EXP] = 1;
> +    xloc = expand_location( EXPR_LOCATION(node) );
> +    if( xloc.file )
> +      {
> +      sprintf(ach, "%s:%d:%d",
> +                  xloc.file,
> +                  xloc.line,
> +                  xloc.column);
> +      rjd_fprintf("expression_location %s\n", ach);
> +      json_namevalue("expression_location", ach);
> +      }
> +    len = TREE_OPERAND_LENGTH(node);
> +
> +    tree node_vars = NULL_TREE;
> +    tree node_body = NULL_TREE;
> +    tree node_block = NULL_TREE;
> +
> +    if( len )
> +      {
> +      json_start_array("operands");
> +      }
> +    for(i = 0; i < len; i++)
> +      {
> +      char temp[32];
> +      sprintf(temp, "operand[%d]", i);
> +
> +      if( code == BIND_EXPR )
> +        {
> +        switch(i)
> +          {
> +          // vars/body/block is the order they appear.  I modify that to
> +          // vars/block/body because that's easier for me to deal with
> when I
> +          // manually graph the connections of the results
> +          case 0:
> +            node_vars = node;
> +            continue;
> +            break;
> +          case 1:
> +            node_body = node;
> +            continue;
> +            break;
> +          case 2:
> +            node_block = node;
> +            continue;
> +            break;
> +          }
> +        }
> +      if( code == COMPONENT_REF )
> +        {
> +        switch(i)
> +          {
> +          case 0:
> +            strcpy(temp,"struct/union");
> +            break;
> +          case 1:
> +            strcpy(temp,"field");
> +            break;
> +          case 2:
> +            strcpy(temp,"offset");
> +            break;
> +          }
> +        }
> +      rjd_subtree(temp, TREE_OPERAND(node, i));
> +      json_comma = ",";
> +      }
> +    if( len )
> +      {
> +      json_finish_array();
> +      }
> +
> +    // Here's where I output the block/vars/body in my preferred order
> +    if(node_block)
> +      {
> +      rjd_subtree("block", TREE_OPERAND(node_block, 2));
> +      }
> +    if(node_vars)
> +      {
> +      rjd_subtree("vars", TREE_OPERAND(node_vars, 0));
> +      }
> +    if(node_body)
> +      {
> +      rjd_subtree("body", TREE_OPERAND(node_body, 1));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_LIST) )
> +    {
> +    processed[TS_LIST] = 1;
> +    rjd_subtree("purpose", TREE_PURPOSE(node));
> +    rjd_subtree("value", TREE_VALUE(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_STATEMENT_LIST) )
> +    {
> +    processed[TS_STATEMENT_LIST] = 1;
> +
> +    // We have a linked list to walk:
> +
> +    int i = 0;
> +    tree_statement_list_node *next = STATEMENT_LIST_HEAD(node);
> +    if( next )
> +      {
> +      json_start_array("statement_list");
> +      }
> +    while( next )
> +      {
> +      sprintf(ach,"statement_list[%d]",i++);
> +      rjd_subtree(ach, next->stmt);
> +
> +      // By rights, this next statement, while not illegal, should
> +      // be regarded as immoral.
> +      next = next->next;
> +      json_comma = ",";
> +      }
> +    if( STATEMENT_LIST_HEAD(node) )
> +      {
> +      json_finish_array();
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_RESULT_DECL) )
> +    {
> +    processed[TS_RESULT_DECL] = 1;
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(DECL_BY_REFERENCE, "by_reference");
> +    ADD_FLAG(DECL_NONSHAREABLE, "nonshareable");
> +    ADD_FLAG(DECL_HAS_VALUE_EXPR_P, "has_value_expr");
> +    ADD_FLAG(SSA_VAR_P, "ssa_name_is_possible");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("result_flags:%s\n",ach);
> +      json_flags("result_flags", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_STRING) )
> +    {
> +    // First, do the text/html string:
> +    strcpy(ach, "");
> +    char ach2[8];
> +    processed[TS_STRING] = 1;
> +    const char *p = TREE_STRING_POINTER(node);
> +    int i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
> +    while(--i >= 0)
> +      {
> +      char ch = *p++;
> +      if(ch >= ' ' && ch < 127)
> +        {
> +        sprintf(ach2, "%c", ch);
> +        }
> +      else
> +        {
> +        sprintf(ach2, "\\%03o", ch & 0xFF);
> +        }
> +      strcat(ach, ach2);
> +      }
> +    rjd_fprintf("string: \"");
> +    rjd_fprintf("%s\"\n", ach);
> +
> +    // Second, do the JSON string:
> +    strcpy(ach, "");
> +    p = TREE_STRING_POINTER(node);
> +    i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
> +    while(--i >= 0)
> +      {
> +      char ch = *p++;
> +      strcpy(ach2, "");
> +      switch( ch )
> +        {
> +        case '\"' :
> +          strcpy(ach2, "\\\"");
> +          break;
> +        case '\\' :
> +          strcpy(ach2, "\\\\");
> +          break;
> +        case '/' :
> +          strcpy(ach2, "/");
> +          break;
> +        case '\b' :
> +          strcpy(ach2, "\\b");
> +          break;
> +        case '\f' :
> +          strcpy(ach2, "\\f");
> +          break;
> +        case '\n' :
> +          strcpy(ach2, "\\n");
> +          break;
> +        case '\r' :
> +          strcpy(ach2, "\\r");
> +          break;
> +        case '\t' :
> +          strcpy(ach2, "\\t");
> +          break;
> +        default:
> +        if(ch >= ' ' && ch < 127)
> +          {
> +          sprintf(ach2, "%c", ch);
> +          }
> +        else
> +          {
> +          sprintf(ach2, "\\u%4.4x", (unsigned int)(ch & 0xFF));
> +          }
> +        }
> +      strcat(ach, ach2);
> +      }
> +    json_namevalue("string", ach);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_IDENTIFIER) )
> +    {
> +    processed[TS_IDENTIFIER] = 1;
> +    rjd_fprintf("identifier: \"%s\"\n", IDENTIFIER_POINTER(node));
> +    json_namevalue("identifier", IDENTIFIER_POINTER(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_BLOCK) )
> +    {
> +    processed[TS_BLOCK] = 1;
> +
> +    sprintf(ach, "%u", BLOCK_NUMBER(node));
> +    rjd_fprintf("block_num: %u\n", ach);
> +    json_namevalue("block_num", ach);
> +
> +    xloc = expand_location(BLOCK_SOURCE_LOCATION(node));
> +    if( xloc.file )
> +      {
> +      rjd_fprintf(ach, "%s:%d:%d",
> +                  xloc.file,
> +                  xloc.line,
> +                  xloc.column);
> +      rjd_fprintf("block_location_start: %s\n", ach);
> +      json_namevalue("block_location_start", ach);
> +      }
> +
> +    xloc = expand_location(BLOCK_SOURCE_END_LOCATION(node));
> +    if( xloc.file )
> +      {
> +      rjd_fprintf(ach, "%s:%d:%d",
> +                  xloc.file,
> +                  xloc.line,
> +                  xloc.column);
> +      rjd_fprintf("block_location_end: %s\n", ach);
> +      json_namevalue("block_location_end", ach);
> +      }
> +
> +    if( BLOCK_DIE(node) )
> +      {
> +      rjd_fprintf("DWARF lexical block(pointer) %p\n", BLOCK_DIE(node));
> +      }
> +
> +    rjd_subtree("vars", BLOCK_VARS(node));
> +
> +    len = BLOCK_NUM_NONLOCALIZED_VARS(node);
> +    if( len )
> +      {
> +      json_start_array("nonlocalized_vars");
> +      }
> +    for(i = 0; i < len; i++)
> +      {
> +      char temp[32];
> +      sprintf(temp, "nonlocalized_var[%d]", i);
> +      rjd_subtree(temp, BLOCK_NONLOCALIZED_VAR(node, i));
> +      json_comma = ",";
> +      }
> +    if( len )
> +      {
> +      json_finish_array();
> +      }
> +
> +    rjd_subtree("subblocks", BLOCK_SUBBLOCKS(node));
> +    rjd_subtree("supercontext", BLOCK_SUPERCONTEXT(node));
> +    rjd_subtree("abstract_origin", BLOCK_ABSTRACT_ORIGIN(node));
> +    rjd_subtree("fragment_origin", BLOCK_FRAGMENT_ORIGIN(node));
> +    rjd_subtree("fragment_chain", BLOCK_FRAGMENT_CHAIN(node));
> +    rjd_subtree("chain", BLOCK_CHAIN(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
> +    {
> +    processed[TS_PARM_DECL] = 1;
> +    // The only attribute unique to TS_PARM_DECL is rtx rtl.
> +    // I might process it, if I knew what it was.  RJD 2021-01-29
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_FIELD_DECL) )
> +    {
> +    processed[TS_FIELD_DECL] = 1;
> +    rjd_subtree("offset", DECL_FIELD_OFFSET(node));
> +    rjd_subtree("bit_field_type", DECL_BIT_FIELD_TYPE(node));
> +    rjd_subtree("qualifier", DECL_QUALIFIER(node));
> +    rjd_subtree("bit_offset", DECL_FIELD_BIT_OFFSET(node));
> +    rjd_subtree("fcontext", DECL_FCONTEXT(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_VAR_DECL) )
> +    {
> +    processed[TS_VAR_DECL] = 1;
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_PUBLIC,"public");
> +    ADD_FLAG(DECL_IN_TEXT_SECTION,"in_text_section");
> +    ADD_FLAG(DECL_IN_CONSTANT_POOL,"in_constant_pool");
> +    ADD_FLAG(DECL_HARD_REGISTER,"hard_register");
> +    ADD_FLAG(DECL_HAS_INIT_PRIORITY_P,"init_priority_p");
> +    ADD_FLAG(DECL_HAS_DEBUG_EXPR_P,"debug_expr_is_from");
> +    ADD_FLAG(VAR_DECL_IS_VIRTUAL_OPERAND,"is_virtual_operand");
> +    ADD_FLAG(DECL_NONLOCAL_FRAME,"non_local_frame_structure");
> +    ADD_FLAG(DECL_NONALIASED,"non_aliased");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("var_decl_flags:%s\n",ach);
> +      json_flags("var_decl_flags", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_DECL) )
> +    {
> +    processed[TS_TYPE_DECL] = 1;
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_PUBLIC,"public");
> +    ADD_FLAG(TYPE_DECL_SUPPRESS_DEBUG,"suppress_debug");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("type_decl_flags:%s\n",ach);
> +      json_flags("type_decl_flags", ach);
> +      }
> +    rjd_subtree("original_type", DECL_ORIGINAL_TYPE(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_CONST_DECL) )
> +    {
> +    processed[TS_CONST_DECL] = 1;
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TRANSLATION_UNIT_DECL) )
> +    {
> +    processed[TS_TRANSLATION_UNIT_DECL] = 1;
> +    if( TRANSLATION_UNIT_LANGUAGE(node) )
> +      {
> +      rjd_fprintf("language: %s\n", TRANSLATION_UNIT_LANGUAGE(node));
> +      json_namevalue("language", TRANSLATION_UNIT_LANGUAGE(node));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_LABEL_DECL) )
> +    {
> +    processed[TS_LABEL_DECL] = 1;
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_ADDRESSABLE,"outside_stack_levels");
> +    ADD_FLAG(FORCED_LABEL,"forced_label");
> +    ADD_FLAG(FALLTHROUGH_LABEL_P,"fallthrough_allowed");
> +    ADD_FLAG(SWITCH_BREAK_LABEL_P,"switch_break");
> +    ADD_FLAG(DECL_NONLOCAL,"nonlocal_permitted");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("label_decl_flags:%s\n",ach);
> +      json_flags("label_decl_flags", ach);
> +      }
> +
> +    rjd_subtree("label_context", DECL_CONTEXT(node));
> +
> +    sprintf(ach, "%d", LABEL_DECL_UID(node));
> +    rjd_fprintf("label_uid: %s\n", ach);
> +    json_namevalue("label_uid", ach);
> +
> +    sprintf(ach, "%d", EH_LANDING_PAD_NR(node));
> +    rjd_fprintf("eh_landing_pad: %s\n", ach);
> +    json_namevalue("eh_landing_pad", ach);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_CONSTRUCTOR) )
> +    {
> +    processed[TS_CONSTRUCTOR] = 1;
> +
> +    if( CONSTRUCTOR_NO_CLEARING(node) )
> +      {
> +      rjd_fprintf("no_clearing: ON\n");
> +      json_namevalue("no_clearing", "true", NOT_QUOTED);
> +      }
> +    else
> +      {
> +      rjd_fprintf("no_clearing: off\n");
> +      json_namevalue("no_clearing", "false", NOT_QUOTED);
> +      }
> +
> +    char temp[32];
> +
> +    if( CONSTRUCTOR_NELTS(node) )
> +      {
> +      json_start_array("constructor_elts");
> +      }
> +    for(size_t i=0; i<CONSTRUCTOR_NELTS(node); i++)
> +      {
> +      constructor_elt *c_elt = CONSTRUCTOR_ELT(node, i);
> +
> +      sprintf(temp, "constructor_elt[%d].index",(int)i);
> +      rjd_subtree(temp, c_elt->index);
> +
> +      sprintf(temp, "constructor_elt[%d].value",(int)i);
> +      rjd_subtree(temp, c_elt->value);
> +
> +      int node_number_i = find_node_in_nodes(c_elt->index);
> +      sprintf(ach, "{\"constructor\":{\"index_node\":%d,",
> node_number_i);
> +      json_newline();
> +      json_fprintf("%s", ach);
> +      json_comma = ",";
> +
> +      int node_number_v = find_node_in_nodes(c_elt->value);
> +      sprintf(ach, "\"value_node\":%d}}", node_number_v);
> +      json_fprintf("%s", ach);
> +      json_comma = ",";
> +      }
> +    if( CONSTRUCTOR_NELTS(node) )
> +      {
> +      json_finish_array();
> +      }
> +    }
> +
> +  // We put this next statement at the end, because it's nice when CHAIN
> +  // is the last thing we see:
> +  if( CODE_CONTAINS_STRUCT(code, TS_COMMON) )
> +    {
> +    processed[TS_COMMON] = 1;
> +    rjd_subtree("chain", TREE_CHAIN(node));
> +    }
> +
> +  for(int i=0; i<64; i++)
> +    {
> +    if( required[i] && !processed[i])
> +      {
> +      /*  Arriving here means that you have encountered a tree struct
> +          that you don't know how to handle.  This happened to me just
> +          now.  The error message I see is
> +
> +              structure 'parm decl': NOT PROCESSED!!!!
> +
> +          What to do?
> +
> +          Well, the first thing I do is look in gcc/treestruct.def for
> +          "parm decl".  I find that text in this line:
> +
> +              DEFTREESTRUCT(TS_PARM_DECL, "parm decl")
> +
> +          This tells me that the tree struct's enum code is TS_PARM_DECL
> +
> +          Next, I look in gcc/tree-core.h for the text "TS_PARM_DECL". I
> find
> +          it in this line:
> +
> +              struct tree_parm_decl  GTY ((tag("TS_PARM_DECL")))
> parm_decl;
> +
> +          This tells me the structure I need to handle is
> "tree_parm_decl".
> +          The declaration of that structure is found earlier in
> gcc/tree-core.h
> +
> +              struct GTY(()) tree_parm_decl {
> +              struct tree_decl_with_rtl common;
> +              rtx incoming_rtl; };
> +
> +          That structure has a substructure "tree_decl_with_rtl", which
> will
> +          require  its one handler.  The structure has its own specific
> +          attribute "rtx incoming_rtl", which we might, or might not want
> to
> +          use as a source of display information.  But, in any case, we
> do need
> +          to add code up above for handling TS_PARM_DECL structures.
> You'll
> +          find that code in the block starting with
> +
> +              if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
> +                  {...}
> +
> +          For each attribute in the TS_xxx that you are working with,
> there is
> +          an accessor macro in gcc/tree.h for getting at that data.  You
> need
> +          to be using those macros; there are a lot of hints and
> information
> +          about the attribute.  For example, by searching for
> ".incoming_rtl"
> +          -- note the leading dot -- you find this commented entry in
> +          gcc/tree.h:
> +
> +              /_* For PARM_DECL, holds an RTL for the stack slot or
> register
> +                 where the data was actually passed.  *_/
> +              #define DECL_INCOMING_RTL(NODE) \
> +               (PARM_DECL_CHECK(NODE)->parm_decl.incoming_rtl)
> +
> +          We are processing a PARM_DECL node, which means that the macro
> +          invocation:
> +
> +              DECL_INCOMING_RTL(node)
> +
> +          will safely return "incoming_rtl", because the PARM_DECL_CHECK
> macro
> +          won't throw an error.
> +
> +          And that's how this error message is eliminated.
> +          */
> +
> +      rjd_fprintf("structure \'%s\': ",ts_enum_names[i]);
> +      rjd_fprintf("NOT PROCESSED!!!!\n" );
> +      fprintf(stderr, "structure \'%s\': ",ts_enum_names[i]);
> +      fprintf(stderr, "NOT PROCESSED!!!!\n" );
> +      exit(1);
> +      }
> +    }
> +
> +  if( phase==2 && fhtml )
> +    {
> +    fprintf(fhtml, "</p>\n");
> +    }
> +}
> +
> +void
> +dump_generic_nodes(const char *filename, tree root)
> +{
> +  /*  This function is called at the beginning of
> gimplify_function_tree() in
> +   *  gimplify.cc.  The 'root' parameter is supposed to be a func_decl
> node.
> +   *
> +   *  When the flag_dump_generic_nodes flag is true, that means the
> compiler was
> +   *  invoked with -fdump-generic-nodes, and that means we jump into
> action.
> +   *
> +   *  We create two files based on filename.  When filename is NULL, it
> gets
> +   *  replaced with the name of the function as extracted from the
> func_decl
> +   *  node.
> +   *
> +   *  filename.nodes is a text file.  filename.nodes.html has the same
> +   *  information in a hypertext file with in-file links to referenced
> nodes.
> +   *
> +   *  The output is a listing of all of the function's nodes and their
> +   *  attributes.  Here is an example of the first two nodes of a typical
> file:
> +
> +***********************************This is NodeNumber0
> +(0x7f12e13b0d00) NodeNumber0
> +tree_code: function_decl
> +tree_code_class: tcc_declaration
> +base_flags: static public
> +type: NodeNumber1 function_type
> +name: NodeNumber6410 identifier_node "main"
> +context: NodeNumber107 translation_unit_decl "bigger.c"
> +source_location: bigger.c:7:5
> +uid: 3663
> +initial(bindings): NodeNumber6411 block
> +machine_mode: QI(15)
> +align: 8
> +warn_if_not_align: 0
> +pt_uid: 3663
> +raw_assembler_name: NodeNumber6410 identifier_node "main"
> +visibility: default
> +result: NodeNumber6412 result_decl
> +function(pointer): 0x7f12e135d508
> +arguments: NodeNumber6413 parm_decl "argc"
> +saved_tree(function_body): NodeNumber6417 statement_list
> +function_code: 0
> +function_flags: public no_instrument_function_entry_exit
> +***********************************This is NodeNumber1
> +(0x7f12e13b3d20) NodeNumber1
> +tree_code: function_type
> +tree_code_class: tcc_type
> +machine_mode: QI(15)
> +type: NodeNumber2 integer_type
> +address_space:0
> +size(in bits): NodeNumber55 uint128 8
> +size_unit(in bytes): NodeNumber12 uint64 1
> +uid: 1515
> +precision: 0
> +contains_placeholder: 0
> +align: 8
> +warn_if_not_align: 0
> +alias_set_type: -1
> +canonical: NodeNumber1 function_type
> +main_variant: NodeNumber1 function_type
> +values: NodeNumber6408 tree_list
> +***********************************This is NodeNumber3
> +
> +  */
> +
> +  if( flag_dump_generic_nodes )
> +    {
> +    GV_number_of_nodes = 0;
> +    char achFilename[1024];
> +    if( !filename )
> +      {
> +      // If, perchance, the root is a decl, use its
> +      // name field as the root of the output file.  This means
> +      // that each function_decl will result in its output file:
> +      // main.tags, foo.tags, bar.tags, and so on.
> +
> +      if( DECL_P(root) )
> +        {
> +        filename = IDENTIFIER_POINTER(DECL_NAME(root));
> +        }
> +      }
> +
> +    if( filename )
> +      {
> +      // Find the root name:
> +      const char *p1 = strrchr(filename,'/');
> +      if( p1 )
> +        {
> +        p1 += 1;
> +        }
> +      else
> +        {
> +        p1 = filename;
> +        }
> +      strcpy(achFilename,filename);
> +      char *p2 = strrchr(achFilename,'.');
> +      if( p2 )
> +        {
> +        *p2 = '\0';
> +        }
> +      strcat(achFilename,".nodes");
> +      ftext = fopen(achFilename, "w");
> +      if( !ftext )
> +          {
> +          error("Unable to open %s", achFilename);
> +          }
> +      // For reasons I don't understand this setvbuf is necessary.
> Without
> +      // it the vfprintf calls here in this routine interfere with
> +      // printf calls elsewhere.
> +      setvbuf(ftext, NULL, _IONBF, 0);
> +
> +      strcat(achFilename,".html");
> +      fhtml = fopen(achFilename, "w");
> +      if( !ftext )
> +          {
> +          error("Unable to open %s", achFilename);
> +          }
> +      setvbuf(fhtml, NULL, _IONBF, 0);
> +
> +      char title[1024];
> +      strcpy(title, "GENERIC for ");
> +      strcat(title, filename);
> +      strcat(title, "()");
> +
> +      html_boilerplate(title);
> +
> +      strcpy(achFilename,filename);
> +      p2 = strrchr(achFilename,'.');
> +      if( p2 )
> +        {
> +        *p2 = '\0';
> +        }
> +      strcat(achFilename,".json");
> +      fjson = fopen(achFilename, "w");
> +      if( !fjson )
> +          {
> +          error("Unable to open %s", achFilename);
> +          }
> +      // For reasons I don't understand this setvbuf is necessary.
> Without
> +      // it the vfprintf calls here in this routine interfere with
> +      // printf calls elsewhere.
> +      setvbuf(fjson, NULL, _IONBF, 0);
> +      }
> +    else
> +      {
> +      fprintf(  stderr,
> +                "%s(): Needs either a filename or a DECL_NODE\n",
> +                __func__);
> +      gcc_assert(false);
> +      }
> +
> +    if( ftext )
> +      {
> +      phase = 1;
> +      rjd_print_node(root);
> +
> +      phase = 2;
> +
> +      // Here is the JSON "header"
> +      json_level = 0;
> +      json_comma = ",";
> +      json_indent();
> +      json_fprintf("{\n");
> +      json_fprintf("\"version\":\"1.0\"");
> +
> +      json_newline();
> +      json_fprintf("\"node_count\":%d", GV_number_of_nodes);
> +
> +      json_comma = ",";
> +      json_newline();
> +      json_fprintf("\"nodes\":\n");
> +      json_fprintf("[");
> +
> +      json_level += 1;
> +      json_comma = "";
> +      for(int i=0; i<GV_number_of_nodes; i++)
> +        {
> +        json_newline();
> +        json_comma = ",";
> +        json_fprintf("{\n");
> +        json_indent();
> +        json_fprintf("\"node\":%d", i);
> +
> +        rjd_print_node(nodes[i].this_node);
> +
> +        json_fprintf("\n");
> +        json_indent();
> +        json_fprintf("}");
> +        }
> +      json_level -= 1;
> +
> +      // Here is the JSON "trailer, that ties off the header"
> +      json_fprintf("\n");
> +      json_fprintf("]\n");
> +      json_fprintf("}\n");
> +      json_level = 1;
> +
> +      fclose(ftext);
> +      ftext = NULL;
> +
> +      html_boilerplate();
> +      fclose(fhtml);
> +      ftext = NULL;
> +
> +      fclose(fjson);
> +      ftext = NULL;
> +      }
> +    }
> +}
> diff --git a/gcc/dump-generic-nodes.h b/gcc/dump-generic-nodes.h
> new file mode 100644
> index 00000000000..ffe18ce2aa5
> --- /dev/null
> +++ b/gcc/dump-generic-nodes.h
> @@ -0,0 +1,26 @@
> +/* Various declarations for language-independent pretty-print
> subroutines.
> +   Copyright (C) 2002-2024 Free Software Foundation, Inc.
> +   Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_PRINT_GIMPLE_NODES_H
> +#define GCC_PRINT_GIMPLE_NODES_H
> +
> +extern void dump_generic_nodes(const char *file_name, tree fndecl);
> +
> +#endif
> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> index 7f79b3cc7e6..7d3c23bcb80 100644
> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "omp-offload.h"
>  #include "context.h"
>  #include "tree-nested.h"
> +#include "dump-generic-nodes.h"
>
>  /* Hash set of poisoned variables in a bind expr.  */
>  static hash_set<tree> *asan_poisoned_variables = NULL;
> @@ -19232,6 +19233,8 @@ gimplify_function_tree (tree fndecl)
>
>    gcc_assert (!gimple_body (fndecl));
>
> +  dump_generic_nodes(NULL, fndecl);
> +
>    if (DECL_STRUCT_FUNCTION (fndecl))
>      push_cfun (DECL_STRUCT_FUNCTION (fndecl));
>    else
> --
> 2.34.1
>
Robert Dubner Feb. 27, 2024, 9:20 p.m. UTC | #2
Richard,

Thank you very much for your comments.

When I set out to create the capability, I had a "specification" in mind.

I didn't have a clue how to create a GENERIC tree that could be fed to the 
middle end in a way that would successfully result in an executable.  And I 
needed to be able to do that in order to proceed with the project of 
creating a COBOL front end.

So, I came up with the idea of using GCC to compile simple programs, and to 
hook into the compiler to examine the trees fed to the middle end, and to 
display those trees in the human-readable format I needed to understand 
them.  And that's what I did.

My first incarnation generated pure text files, and I used that to get 
going.

After a while I realized that when I used the output file, I was spending a 
lot of time searching through the text files.  And I had the brainstorm! 
Hyperlinks!  HTML files!  We have the technology!  So, I created the .HTML 
files as well.

I found this useful to the point of necessity in order to learn how to 
generate the GENERIC trees.  I believe it would be equally useful to the 
next developer who, for whatever reason, needs to understand, on a "You need 
to learn the alphabet before you can learn how to read" level, what the 
middle end requires from a GENERIC tree generated by a front end.

But I've never used it on a complex program. I've used it only to learn how 
to create the GENERIC nodes for very particular things, and so I would use 
the -fdump-generic-nodes feature on a very simple C program that 
demonstrated, in isolation, the feature I needed.  Once I figured it out, I 
would create front end C routines or macros that used the tree.h/tree.cc 
features to build those GENERIC trees, and then I would move on.

I decided to offer it up here, in order to to learn how to create patches 
and to get
to know the people and the process, as well as from the desire to share it. 
And instantly I got the "How about a machine-readable format?" comments. 
Which are reasonable.  So, because it wasn't hard, I hacked at the existing 
code to create a JSON output.  (But I remind you that up until now, nobody 
seems to have needed a JSON representation.)

And your observation that the human readable representation could be made 
from the JSON representation is totally accurate.

But that wasn't my specification.  My specification was "A tool so that a 
human being can examine a simple GENERIC tree to learn how it's done."

But it seems to me that we are now moving into the realm of a new 
specification.

Said another way:  To go from "A human readable representation of a simple 
GENERIC tree" to "A machine readable JSON representation of an arbitrarily 
complex GENERIC tree, from which a human readable representation can be 
created" means, in effect, starting over on a different project that I don't 
need.  I already *have* a project that I am working on -- the COBOL front 
end.

The complexity of GENERIC trees is, in my experienced opinion, an obstacle 
for the creation of front ends.  The GCC Internals document has a lot of 
information, but to go from it to a front end is like using the maintenance 
manual for an F16 fighter to try to learn to fly the aircraft.

The program "main(){}" generates a tree with over seventy nodes.  I see no 
way to document why that's true; it's all arbitrary in the sense that "this 
is how GCC works".  -fdump-generic-nodes made it possible for me to figure 
out how those nodes are connected and, thus, how to create a new front end. 
I figure that other developers might find it useful, as well.

I guess I am saying that I am not, at this time, able to work on a whole 
different tool.  I think what I have done so far does something useful that 
doesn't seem to otherwise exist in GCC.

I suppose the question for you is, "Is it useful enough?"

I won't be offended if the answer is "No" and I hope you won't be offended 
by my not having the bandwidth to address your very thoughtful and valid 
observations about how it could be better.

-----Original Message-----
From: Richard Biener <richard.guenther@gmail.com>
Sent: Tuesday, February 27, 2024 04:11
To: Robert Dubner <rdubner@symas.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] developer option: -fdump-generic-nodes; initial 
incorporation

On Thu, Feb 22, 2024 at 5:46 PM Robert Dubner <rdubner@symas.com> wrote:
>
> As part of an effort to learn how create a GENERIC tree in order to
> implement a
> COBOL front end, I created the dump_generic_nodes(), which accepts a
> function_decl at the point it is provided to the middle end.  The routine
> generates three files.  One is ASCII, the second is HTML; they contain the
> tree
> in a human-readable form.  The third is JSON.
>
> This commit modifies common.opt to accept the -fdump-generic-nodes
> command-line
> option, creates the dump-generic-nodes.cc and .h files to implement it,
> and
> inserts a call to the dump_generic_nodes() function near the top of
> gimplify_function_tree() in gcc/gimplify.cc

While I think that's good and probably the best you can do in language
independent code GCCs 'GENERIC' is inherently frontend specific
(only GIMPLE is no longer).  The gimplifier you hook into eventually
relies on the 'gimplify_expr' language hook to deal with frontend specific
tree codes and there might be auxiliary info in the language-specific
portions of types and decls (DECL_LANG_SPECIFIC, TYPE_LANG_SPECIFIC).

That's a caveat only, it might mean that in some cases a
'dump_generic_to_json' language hook might be a nice thing to have.
Note this is likely only a "problem" for frontends not having their own
internal AST representation they lower to GENERIC;  first and foremost
the C and C++ language frontends - I'm not sure of others here, but
the presence of

./ada/gcc-interface/ada-tree.def
./c/c-tree.def
./cp/cp-tree.def
./d/d-tree.def
./m2/m2-tree.def
./objc/objc-tree.def

suggests that "issue" might be wide-spread.

> This patch has been tested on X86_64-linux-gnu.  I haven't tried to
> provide
> testcases for the automated system because 1) I haven't learned how to do
> that,
> and 2), I am not sure how to test this feature.  On the one hand, the
> compiler
> isn't affected when the switch isn't present; when it is present it seems
> to
> work on simple source code.

I think this is a useful feature.  I do wonder whether it makes more sense
to only implement JSON dumping inside the compiler and leave html
and text output to postprocessing that.  That avoids diverging information
detail and should reduce the amount of code to maintain.  Scripts to
perform analysis/transform of such JSON could be contributed into
the contrib/ directory.

To follow general filename standards for auxiliary files you should
prepend the filename(s) you dump to with 'aux_base_name', the
chance of accidentially trashing users files is then reduced a lot.

It might be that with JSON it's reasonable to output a single file per TU
only, avoiding issues with say C++ function overloading and possible
clashes on filenames.

https://gcc.gnu.org/contribute.html#standards lists some bits that
we expect to help common appearance of code and maintainance.
For example a lot of your functions have no function-level comment
documenting them.

> Legal requirements:  The FSF has on file an "employer disclaimer" for me.
>
> I am using the "Signed off by" tag in an attempt to cover the legal bases;
> I
> trust I will be apprised of anything else that needs to be done.

I think that's OK.

> gcc/ChangeLog:
>
>         * developer options: -fdump-generic-nodes initial incorporation
>
> Signed-off-by: Robert Dubner <rdubner@symas.com>
> ---
>  gcc/Makefile.in           |    3 +-
>  gcc/common.opt            |    4 +
>  gcc/dump-generic-nodes.cc | 1958 +++++++++++++++++++++++++++++++++++++
>  gcc/dump-generic-nodes.h  |   26 +
>  gcc/gimplify.cc           |    3 +
>  5 files changed, 1993 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/dump-generic-nodes.cc
>  create mode 100644 gcc/dump-generic-nodes.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index a74761b7ab3..81922b0884c 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1441,6 +1441,7 @@ OBJS = \
>         domwalk.o \
>         double-int.o \
>         dse.o \
> +       dump-generic-nodes.o \
>         dumpfile.o \
>         dwarf2asm.o \
>         dwarf2cfi.o \
> @@ -3857,7 +3858,7 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H)
> coretypes.h $(TM_H) \
>    hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h
> \
>    lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \
>    pass-instances.def params.list $(srcdir)/../include/gomp-constants.h \
> -  $(EXPR_H) $(srcdir)/analyzer/*.h
> +  $(EXPR_H) $(srcdir)/analyzer/*.h dump-generic-nodes.h
>
>  # generate the 'build fragment' b-header-vars
>  s-header-vars: Makefile
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 51c4a17da83..751b9b1f0cc 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1583,6 +1583,10 @@ fdump-passes
>  Common Var(flag_dump_passes) Init(0)
>  Dump optimization passes.
>
> +fdump-generic-nodes
> +Common Var(flag_dump_generic_nodes) Init(0)
> +Dump GENERIC trees for each function in three files: <funcname>.nodes,
> <funcname>.nodes.html, and <funcname>.json
> +
>  fdump-unnumbered
>  Common Var(flag_dump_unnumbered)
>  Suppress output of instruction numbers, line number notes and addresses
> in debugging dumps.
> diff --git a/gcc/dump-generic-nodes.cc b/gcc/dump-generic-nodes.cc
> new file mode 100644
> index 00000000000..d44119116d2
> --- /dev/null
> +++ b/gcc/dump-generic-nodes.cc
> @@ -0,0 +1,1958 @@
> +/* Prints out a tree of generic/gimple nodes in human readable form, both
> in
> +   straight text and in HTML. The entry point is dump_generic_nodes().
> +
> +   Copyright(C) 1990-2024 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or(at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tm.h"
> +#include "tree.h"
> +#include "cgraph.h"
> +#include "diagnostic.h"
> +#include "varasm.h"
> +#include "print-rtl.h"
> +#include "stor-layout.h"
> +#include "langhooks.h"
> +#include "tree-iterator.h"
> +#include "gimple-pretty-print.h"
> +#include "tree-cfg.h"
> +#include "dumpfile.h"
> +
> +#undef DEFTREESTRUCT
> +#define DEFTREESTRUCT(VAL, NAME) NAME,
> +static const char *ts_enum_names[] =
> +  {
> +  #include "treestruct.def"
> +  };
> +#undef DEFTREESTRUCT
> +
> +#define ADD_FLAG(accessor,text)if(accessor(node)){strcat(ach," " text);}
> +
> +static FILE *ftext = NULL;
> +static FILE *fhtml = NULL;
> +static FILE *fjson = NULL;
> +
> +static int json_level = 0;
> +static const char *json_comma;
> +static const int spaces_per_indent = 2;
> +
> +static void rjd_print_node(tree node);
> +
> +static int phase = 1;
> +
> +/* Define the hash table of nodes already seen.
> +   Such nodes are not repeated; brief cross-references are used.  */
> +
> +struct TREE
> +  {
> +  tree this_node;
> +  int     seen;
> +  } ;
> +
> +static struct TREE *nodes = NULL;
> +static int GV_size_of_tree = 0;
> +static int GV_number_of_nodes = 0;
> +
> +static void
> +json_fprintf(const char *format, ...) __attribute__ ((format (printf, 1,
> 2)));
> +
> +static void
> +json_fprintf(const char *format, ...)
> +{
> +  char ach1[2048];
> +  if( phase == 2 )
> +    {
> +    va_list args;
> +    va_start(args, format);
> +    vsnprintf(ach1, sizeof(ach1), format, args);
> +    va_end(args);
> +    fprintf(fjson, "%s", ach1);
> +    }
> +}
> +
> +static void
> +json_indent()
> +{
> +  for(int i=0; i<json_level*spaces_per_indent; i++)
> +    {
> +    json_fprintf(" ");
> +    }
> +}
> +
> +static void
> +json_newline()
> +{
> +  json_fprintf("%s\n", json_comma);
> +  json_indent();
> +}
> +
> +static int
> +find_node_in_nodes(tree node)
> +{
> +  // This is an O(n^2) abomination.  At a suitable time, replace it with
> a
> +  // hashed map!
> +  int retval = -1;
> +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> +  for(int i=0; i<GV_number_of_nodes; i++)
> +    {
> +    if( nodes[i].this_node == node )
> +      {
> +      retval = i;
> +      break;
> +      }
> +    }
> +  return retval;
> +}
> +
> +static int
> +add_node_to_nodes(tree node)
> +{
> +  // Assume we know the node isn't there yet:
> +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> +  if( GV_number_of_nodes == GV_size_of_tree )
> +    {
> +    // We've run out of room.  Double the number of nodes:
> +    GV_size_of_tree *= 2;
> +    if( GV_size_of_tree == 0 )
> +      {
> +      GV_size_of_tree = 64;
> +      }
> +    struct TREE *newnodes =
> +               (struct TREE *)xmalloc( GV_size_of_tree * sizeof(struct
> TREE) );
> +    memcpy(newnodes,nodes, GV_number_of_nodes * sizeof(struct TREE));
> +    free(nodes);
> +    nodes = newnodes;
> +    }
> +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> +  nodes[GV_number_of_nodes].this_node = node;
> +  nodes[GV_number_of_nodes].seen = 0;
> +  GV_number_of_nodes += 1;
> +  return GV_number_of_nodes-1;
> +}
> +
> +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
> +
> +static void
> +rjd_fprintf(const char *format, ...)
> +{
> +  // NOTE: I painfully learned that mixing fprintf() and vfprintf() calls
> +  // can result in hard-to-find memory allocation bugs.  Probably worth
> +  // tracking down, but the the problem shows up in the libc6 library
> +  // routines.
> +  char ach1[2048];
> +  char ach2[4196];
> +  char ach3[4196];
> +  if( phase == 2 )
> +    {
> +    va_list args;
> +    va_start(args, format);
> +    vsnprintf(ach1, sizeof(ach1), format, args);
> +    va_end(args);
> +
> +    fprintf(ftext, "%s", ach1);
> +
> +    // Copy ach1 to ach2, replacing '\n' with "<br>"
> +    char *p1 = ach1;
> +    char *p2 = ach2;
> +
> +    while( *p1 )
> +        {
> +        if(*p1 == '\n')
> +          {
> +          p1 += 1;
> +          *p2 = '\0';
> +          strcat(p2, "<br>");
> +          p2 = ach2 + strlen(ach2);
> +          }
> +        else
> +          {
> +          *p2++ = *p1++;
> +          }
> +        }
> +    size_t ach1_length = strlen(ach1);
> +
> +    // To make the HTML more-or-less readable, if ach1 ends with a
> newline,
> +    // make ach2 end with a newline as well.
> +    if( ach1_length && ach1[ach1_length-1] == '\n' )
> +      {
> +      *p2++ = '\n';
> +      }
> +    *p2++ = '\0';
> +
> +    // We now look for NodeNumberNNN and replace it with
> +    // <a href="#NodeNumberNNN">NodeNumberNNN</a>
> +
> +    char *pleft = strstr(ach2, "NodeNumber");
> +    if( pleft )
> +      {
> +      char *pright = pleft + strlen("NodeNumber");
> +      while( *pright >= '0' && *pright <= '9' )
> +        {
> +        pright += 1;
> +        }
> +      memset(ach3, 0, sizeof(ach3));
> +      memcpy(ach3, ach2, pleft - ach2);
> +      strcat(ach3, "<a href=\"#" );
> +      char *p = ach3 + strlen(ach3);
> +      memcpy(p, pleft, pright-pleft);
> +      strcat(ach3,"\">");
> +      p = ach3 + strlen(ach3);
> +      memcpy(p, pleft, pright-pleft);
> +      p = ach3 + strlen(ach3);
> +      strcat(ach3,"</a>");
> +      p = ach3 + strlen(ach3);
> +      strcpy(p, pright);
> +      strcpy(ach2, ach3);
> +      }
> +
> +    fprintf(fhtml, "%s", ach2);
> +    }
> +}
> +
> +#define NOT_QUOTED false
> +static void
> +json_namevalue(const char *name, const char *value, bool quoted = true)
> +{
> +  if( phase == 2 )
> +    {
> +    json_newline();
> +    if( quoted )
> +      {
> +      json_fprintf("\"%s\":\"%s\"", name, value);
> +      }
> +    else
> +      {
> +      json_fprintf("\"%s\":%s", name, value);
> +      }
> +    }
> +}
> +
> +static void
> +html_boilerplate(char *title=NULL)
> +{
> +  if( fhtml )
> +    {
> +    if( title )
> +      {
> +      fprintf(fhtml,
> +              "<!DOCTYPE html>\n"
> +              "<html lang=\"en\">\n"
> +              "  <head>\n"
> +              "    <meta charset=\"utf-8\">\n"
> +              "    <title>%s</title>\n"
> +              "    <style>\n"
> +              "    body\n"
> +              "        {\n"
> +              "        font-family: courier, serif;\n"
> +              "        font-size: 13px;\n"
> +              "        }\n"
> +              "    </style>\n"
> +              "  </head>\n"
> +              "  <body>\n", title);
> +      }
> +    else
> +      {
> +      fprintf(fhtml, "%s",
> +              "  </body>\n"
> +              "</html>\n");
> +      }
> +    }
> +}
> +
> +/* Print a node in brief fashion*/
> +
> +static void
> +print_name(const char *prefix, tree node)
> +{
> +  if( node && TREE_CODE_CLASS(TREE_CODE(node)) == tcc_declaration )
> +    {
> +    tree name = DECL_NAME(node);
> +    if( name )
> +      {
> +      rjd_fprintf("%s\"%s\"", prefix, IDENTIFIER_POINTER(name));
> +      }
> +    }
> +}
> +
> +static void
> +rjd_subtree(const char *subtree_name, tree subtree)
> +{
> +  if( subtree )
> +    {
> +    if( phase == 1 )
> +      {
> +      // Make sure the subtree is in the list of nodes.  And, yes, this
> +      // is a potential headachy recursivy thingummy.  But computers are
> +      // good at that.
> +      rjd_print_node(subtree);
> +
> +      int subtree_node_number = find_node_in_nodes(subtree);
> +      if( subtree_node_number == -1 )
> +        {
> +        printf("Run in circles, scream and shout!\n");
> +        exit(1);
> +        }
> +
> +      }
> +    else if( phase == 2 )
> +      {
> +      char ach[512];
> +      int node_number = find_node_in_nodes(subtree);
> +      if( node_number >= 0 )
> +        {
> +        rjd_fprintf("%s: NodeNumber%d", subtree_name, node_number);
> +
> +        // Handle a few subthings that are very common, and useful to
> see:
> +        gcc_assert(node_number < GV_number_of_nodes
> +                   && GV_number_of_nodes <= GV_size_of_tree);
> +        tree subnode = nodes[node_number].this_node;
> +        if( subnode )
> +          {
> +          enum tree_code subcode = TREE_CODE(subnode);
> +
> +          // After the NodeNumber, print the code name.  Unless it is an
> +          // integer_cst, because we convert that to int32 or uint64
> later.
> +          if( subcode != INTEGER_CST )
> +            {
> +            rjd_fprintf(" %s", get_tree_code_name(subcode));
> +            }
> +
> +          if( subcode == IDENTIFIER_NODE && IDENTIFIER_POINTER(subnode) )
> +            {
> +            rjd_fprintf(" \"%s\"", IDENTIFIER_POINTER(subnode));
> +            }
> +
> +          print_name(" ", subnode);
> +
> +          if( subcode == INTEGER_CST )
> +            {
> +            tree int_cst_type = TREE_TYPE(subnode);
> +            tree_code int_cst_type_code = TREE_CODE(int_cst_type);
> +
> +            if( int_cst_type_code == POINTER_TYPE )
> +              {
> +              rjd_fprintf(" pointer");
> +              }
> +            else if( int_cst_type_code == INTEGER_TYPE)
> +              {
> +              // The int_cst_type is an integer_type, so...
> +              tree min_value = TYPE_MIN_VALUE_RAW(int_cst_type);
> +              print_dec(wi::to_wide(min_value),
> +                        ach,
> +                        TYPE_SIGN(TREE_TYPE(min_value)));
> +              if( strcmp( ach, "0") )
> +                {
> +                rjd_fprintf(" int",ach);
> +                }
> +              else
> +                {
> +                rjd_fprintf(" uint",ach);
> +                }
> +
> +              tree size_in_bits = TYPE_SIZE(int_cst_type);
> +
> +              print_dec(wi::to_wide(size_in_bits),
> +                        ach,
> +                        TYPE_SIGN(TREE_TYPE(size_in_bits)));
> +              rjd_fprintf(ach);
> +
> +              print_dec(wi::to_wide(subnode),
> +                        ach, TYPE_SIGN(TREE_TYPE(subnode)));
> +              }
> +
> +            print_dec(wi::to_wide(subnode),
> +                      ach,
> +                      TYPE_SIGN(TREE_TYPE(subnode)));
> +            rjd_fprintf(" %s",ach);
> +            }
> +
> +          if( subcode == DECL_EXPR )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len )
> +              {
> +              print_name(" ", TREE_OPERAND(subnode, 0));
> +              }
> +            }
> +
> +          if( subcode == CALL_EXPR )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len > 1)
> +              {
> +              tree addr_expression = TREE_OPERAND(subnode, 1);
> +              int len2 = TREE_OPERAND_LENGTH(addr_expression);
> +              if( len2 )
> +                {
> +                print_name(" ", TREE_OPERAND(addr_expression, 0));
> +                }
> +              }
> +            }
> +
> +          if( subcode == ADDR_EXPR  )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len > 0)
> +              {
> +              print_name(" ", TREE_OPERAND(subnode, 0));
> +              }
> +            }
> +
> +          if( subcode == MODIFY_EXPR )
> +            {
> +            int len = TREE_OPERAND_LENGTH(subnode);
> +            if( len > 0)
> +              {
> +              tree target = TREE_OPERAND(subnode, 0);
> +              tree_code target_code = TREE_CODE(target);
> +              tree_code_class target_code_class =
> TREE_CODE_CLASS(target_code);
> +              if( target_code_class == tcc_declaration )
> +                {
> +                print_name(" ", target);
> +                }
> +              else if( target_code == COMPONENT_REF
> +                                      && target_code_class ==
> tcc_reference )
> +                {
> +                int len = TREE_OPERAND_LENGTH(target);
> +                tree structure = NULL_TREE;
> +                tree field     = NULL_TREE;
> +                if( len > 0 )
> +                  {
> +                  structure = TREE_OPERAND(target, 0);
> +                  }
> +                if( len > 1 )
> +                  {
> +                  field = TREE_OPERAND(target, 1);
> +                  }
> +                print_name(" ", structure);
> +                print_name("::", field);
> +                }
> +              }
> +            }
> +
> +          rjd_fprintf("\n");
> +          }
> +        // In contrast, for the JSON output, we just want the node
> number:
> +        // But we need to intervene in the case where the subtree name
> starts
> +        // off with "operand[nnn]".  We are putting them in JSON arraysm,
> so we
> +        // need to eliminate that name
> +        if( strstr(subtree_name, "constructor_elt[") != subtree_name )
> +          {
> +          // constructors are handled separately because they have both
> index
> +          // and value members.  Search for "constructor_elt" to find
> that code.
> +          if(    strstr(subtree_name, "operand[") == subtree_name
> +              || strstr(subtree_name, "tree_vector_element[") ==
> subtree_name
> +              || strstr(subtree_name, "statement_list[") == subtree_name
> +              || strstr(subtree_name, "nonlocalized_var[") ==
> subtree_name
> +              )
> +            {
> +            // We are elminating the subtree name
> +            json_newline();
> +            }
> +          else
> +            {
> +            json_namevalue(subtree_name, "", NOT_QUOTED);
> +            }
> +          json_fprintf("{\"node\":%d}", node_number);
> +          }
> +        }
> +      else
> +        {
> +        // This can happen as a result of certain compilation
> +        // errors.   Just ignore them.
> +        }
> +      }
> +    }
> +  else
> +    {
> +    if( strstr(subtree_name, "operand[") == subtree_name )
> +      {
> +      json_newline();
> +      json_fprintf("{\"node\":null}");
> +      }
> +
> +    }
> +
> +}
> +
> +static void
> +json_flags(const char *name, const char *ach)
> +{
> +  const char *p = ach+1;  // Skip past the initial space
> +  json_namevalue(name, "",  NOT_QUOTED);
> +  json_level += 1;
> +  json_comma = "";
> +  json_fprintf("\n");
> +  json_indent();
> +  json_fprintf("{");
> +
> +  while( *p )
> +    {
> +    const char *pend = strchr(p, ' ');
> +    if( !pend )
> +      {
> +      pend = p + strlen(p);
> +      }
> +    char achName[256];
> +    char *d = achName;
> +    while(p < pend )
> +      {
> +      *d++ = *p++;
> +      }
> +    *d++= '\0';
> +    if( *p == ' ' )
> +      {
> +      p += 1;
> +      }
> +    json_namevalue(achName, "true", NOT_QUOTED);
> +    json_comma = ",";
> +    }
> +  json_fprintf("\n");
> +  json_indent();
> +  json_fprintf("}");
> +
> +  json_level -= 1;
> +  json_comma = ",";
> +}
> +
> +static void
> +json_start_array(const char *name)
> +{
> +  // We will set up a JSON array of operands
> +  json_namevalue(name,"", NOT_QUOTED);
> +  json_fprintf("\n");
> +  json_level += 1;
> +  json_indent();
> +  json_fprintf("[");
> +  json_comma = "";
> +}
> +static void
> +json_finish_array()
> +{
> +  json_fprintf("\n");
> +  json_indent();
> +  json_fprintf("]");
> +  json_level -= 1;
> +  json_comma = ",";
> +}
> +
> +static void
> +rjd_print_node(tree node)
> +{
> +  char ach[4096];
> +  enum tree_code_class tclass;
> +  int len;
> +  int i;
> +  expanded_location xloc;
> +  enum tree_code code;
> +
> +  if(node == 0)
> +    {
> +    // When handled a NULL, just return
> +    return;
> +    }
> +
> +  int node_number = find_node_in_nodes(node);
> +  if( phase == 1 )
> +    {
> +    if( node_number != -1 )
> +      {
> +      // We're building the list of nodes, and we've already processed
> this
> +      // node:
> +      return;
> +      }
> +
> +    // We are building the list of nodes, and this is a new one:
> +    node_number = add_node_to_nodes(node);
> +    }
> +  // From here on out, we know that node_number is valid, whether in
> +  // phase 1 or phase 2
> +
> +  code = TREE_CODE(node);
> +
> +  /* It is unsafe to look at any other fields of a node with ERROR_MARK
> or
> +     invalid code.  */
> +  if(code == ERROR_MARK || code >= MAX_TREE_CODES)
> +    {
> +    rjd_fprintf("This node is unsafe.  The reported TREE_CODE is
> %d\n",code);
> +    return;
> +    }
> +
> +  tclass = TREE_CODE_CLASS(code);
> +
> +  /* Announce the coming of a new node: */
> +  if( phase==2 && fhtml )
> +    {
> +    fprintf(fhtml, "<p id=\"NodeNumber%d\">\n", node_number);
> +    }
> +  rjd_fprintf("***********************************This is
> NodeNumber%d\n",
> +              node_number);
> +
> +  /* Print the NodeNumber in a more canonical form: */
> +  rjd_fprintf("(%p) NodeNumber%d\n", node, node_number);
> +
> +  // Print the tree_code for this node
> +  rjd_fprintf("tree_code: %s\n", get_tree_code_name(code));
> +  json_namevalue("tree_code", get_tree_code_name(code));
> +
> +  // It might be useful to see the class
> +  const char *classtxt;
> +  switch(tclass)
> +    {
> +    case tcc_exceptional:
> +      classtxt = "tcc_exceptional";
> +      break;
> +    case tcc_constant:
> +      classtxt = "tcc_constant";
> +      break;
> +    case tcc_type:
> +      classtxt = "tcc_type";
> +      break;
> +    case tcc_declaration:
> +      classtxt = "tcc_declaration";
> +      break;
> +    case tcc_reference:
> +      classtxt = "tcc_reference";
> +      break;
> +    case tcc_comparison:
> +      classtxt = "tcc_comparison";
> +      break;
> +    case tcc_unary:
> +      classtxt = "tcc_unary";
> +      break;
> +    case tcc_binary:
> +      classtxt = "tcc_binary";
> +      break;
> +    case tcc_statement:
> +      classtxt = "tcc_statement";
> +      break;
> +    case tcc_vl_exp:
> +      classtxt = "tcc_vl_exp";
> +      break;
> +    case tcc_expression:
> +      classtxt = "tcc_expression";
> +      break;
> +    default:
> +      gcc_unreachable();
> +      break;
> +    }
> +  rjd_fprintf("tree_code_class: %s\n", classtxt);
> +  json_namevalue("tree_code_class", classtxt);
> +
> +  int required[64];
> +  int processed[64];
> +  for(int i=0; i<64; i++)
> +    {
> +    required[i] = CODE_CONTAINS_STRUCT(code, i);
> +    processed[i] = 0;
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_BASE) )
> +    {
> +    processed[TS_BASE] = 1;
> +    // There are 16 bits in TS_BASE.  The trouble is, they have
> +    // different meanings for different codes; see the extensive
> +    // comment in tree-core.h
> +    strcpy(ach, "");
> +    if( tclass != tcc_type && TREE_SIDE_EFFECTS(node) )
> +      {
> +      strcat(ach," side_effects");
> +      }
> +    if( tclass != tcc_type && TREE_CONSTANT(node) )
> +      {
> +      strcat(ach," constant");
> +      }
> +    if( TREE_ADDRESSABLE(node) )
> +      {
> +      strcat(ach," addressable");
> +      }
> +    if( TREE_THIS_VOLATILE(node) )
> +      {
> +      strcat(ach," volatile");
> +      }
> +    if( tclass != tcc_type && TREE_READONLY(node) )
> +      {
> +      strcat(ach," readonly");
> +      }
> +    if( TREE_ASM_WRITTEN(node) )
> +      {
> +      strcat(ach," asm_written");
> +      }
> +    if( TREE_NO_WARNING(node) )
> +      {
> +      strcat(ach," nowarning");
> +      }
> +    if( TREE_VISITED(node) )
> +      {
> +      strcat(ach," visited");
> +      }
> +
> +    if( TREE_USED(node) )
> +      {
> +      strcat(ach," used");
> +      }
> +    if( TREE_NOTHROW(node) )
> +      {
> +      strcat(ach," nothrow");
> +      }
> +    if( TREE_STATIC(node) )
> +      {
> +      strcat(ach," static");
> +      }
> +    if( TREE_PUBLIC(node) )
> +      {
> +      strcat(ach," public");
> +      }
> +    if( TREE_PRIVATE(node) )
> +      {
> +      strcat(ach," private");
> +      }
> +    if( TREE_PROTECTED(node) )
> +      {
> +      strcat(ach," protected");
> +      }
> +    if( TREE_DEPRECATED(node) )
> +      {
> +      strcat(ach," deprecated");
> +      }
> +    if( node->base.default_def_flag ) //There isn't a TREE_DEFAULT_DEF
> macro
> +      {
> +      strcat(ach," default_def");
> +      }
> +    if( tclass == tcc_constant )
> +      {
> +      if( TREE_OVERFLOW(node) )
> +        {
> +        strcat(ach," overflow");
> +        }
> +      }
> +    if( tclass == tcc_type )
> +      {
> +      ADD_FLAG(TYPE_UNSIGNED,   "unsigned");
> +      ADD_FLAG(TYPE_PACKED,     "packed");
> +      ADD_FLAG(TYPE_USER_ALIGN, "user_align");
> +      ADD_FLAG(TYPE_NAMELESS,   "nameless");
> +      ADD_FLAG(TYPE_ATOMIC,     "atomic");
> +      }
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("base_flags:%s\n", ach);
> +      json_flags("base_flags", ach);
> +      }
> +    }
> +
> +  if( tclass == tcc_type )
> +    {
> +    sprintf(ach, "%s(%u)",
> +            GET_MODE_NAME(TYPE_MODE(node)),(int)TYPE_MODE(node));
> +    rjd_fprintf("machine_mode: %s\n", ach);
> +    json_namevalue("machine_mode", ach);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPED) )
> +    {
> +    processed[TS_TYPED] = 1;
> +    rjd_subtree("type", TREE_TYPE(node));
> +    if( tclass == tcc_type )
> +      {
> +      sprintf(ach, "%u", (int)TYPE_ADDR_SPACE(node));
> +      rjd_fprintf("address_space:%s\n", ach);
> +      json_namevalue("address_space", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_MINIMAL) )
> +    {
> +    processed[TS_DECL_MINIMAL] = 1;
> +    rjd_subtree("name", DECL_NAME(node));
> +    rjd_subtree("context", DECL_CONTEXT(node));
> +    xloc = expand_location(DECL_SOURCE_LOCATION(node));
> +    if( xloc.file )
> +      {
> +      sprintf(ach, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
> +      rjd_fprintf("source_location: %s\n", ach);
> +      json_namevalue("source_location", ach);
> +      }
> +    sprintf(ach, "%d", DECL_PT_UID(node));
> +    rjd_fprintf("uid: %s\n", ach);
> +    json_namevalue("uid", ach, NOT_QUOTED);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_COMMON) )
> +    {
> +    processed[TS_DECL_COMMON] = 1;
> +    rjd_subtree("size(in bits)", DECL_SIZE(node));
> +    rjd_subtree("size_unit(in bytes)", DECL_SIZE_UNIT(node));
> +
> +    const char *p;
> +    switch(code)
> +      {
> +      case FUNCTION_DECL:
> +        p = "initial(bindings)";
> +        break;
> +      case TRANSLATION_UNIT_DECL:
> +        p = "initial(block)";
> +        break;
> +      case VAR_DECL:
> +        p = "initial value";
> +        break;
> +      default:
> +        p = "initial";
> +        break;
> +      }
> +    rjd_subtree(p, DECL_INITIAL(node));
> +    rjd_subtree("attributes", DECL_ATTRIBUTES(node));
> +    rjd_subtree("abstract_origin", DECL_ABSTRACT_ORIGIN(node));
> +
> +    sprintf(ach, "%s(%u)",
> GET_MODE_NAME(DECL_MODE(node)),(int)DECL_MODE(node));
> +    rjd_fprintf("machine_mode: %s\n", ach);
> +    json_namevalue("machine_mode", ach);
> +
> +    strcpy(ach, "");
> +    if( DECL_NONLOCAL(node) )
> +      {
> +      strcat(ach," nonlocal");
> +      }
> +    if( DECL_VIRTUAL_P(node) )
> +      {
> +      strcat(ach," virtual");
> +      }
> +    if( DECL_IGNORED_P(node) )
> +      {
> +      strcat(ach," ignored");
> +      }
> +    if( DECL_ABSTRACT_P(node) )
> +      {
> +      strcat(ach," abstrac");
> +      }
> +    if( DECL_ARTIFICIAL(node) )
> +      {
> +      strcat(ach," artificial");
> +      }
> +    if( DECL_PRESERVE_P(node) )
> +      {
> +      strcat(ach," preserve");
> +      }
> +    if( code == VAR_DECL && DECL_DEBUG_EXPR(node) )
> +      {
> +      strcat(ach," debug_expr_is_from");
> +      }
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("decl_flags:%s\n",ach);
> +      json_flags("decl_flags", ach);
> +      }
> +
> +    if( code == FIELD_DECL )
> +      {
> +      sprintf(ach, "%u", (int)DECL_OFFSET_ALIGN(node));
> +      rjd_fprintf("offset_align: %s\n", ach);
> +      json_namevalue("offset_align", ach);
> +      }
> +    sprintf(ach, "%u", (int)DECL_ALIGN(node));
> +    rjd_fprintf("align: %u\n", ach);
> +    json_namevalue("align", ach, NOT_QUOTED);
> +
> +    sprintf(ach, "%u", (int)DECL_WARN_IF_NOT_ALIGN(node));
> +    rjd_fprintf("warn_if_not_align: %s\n", ach);
> +    json_namevalue("warn_if_not_align", ach, NOT_QUOTED);
> +
> +    sprintf(ach, "%u", (int)DECL_PT_UID(node));
> +    rjd_fprintf("pt_uid: %s\n", ach);
> +    json_namevalue("pt_uid", ach, NOT_QUOTED);
> +
> +    if( DECL_LANG_SPECIFIC(node) )
> +      {
> +      rjd_fprintf("lang_specific(pointer):
> %p\n",DECL_LANG_SPECIFIC(node));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WRTL) )
> +    {
> +    processed[TS_DECL_WRTL] = 1;
> +    if( DECL_RTL_SET_P(node) )
> +      {
> +      rjd_fprintf("DECL_RTL for NODE has already been set\n");
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WITH_VIS) )
> +    {
> +    processed[TS_DECL_WITH_VIS] = 1;
> +    rjd_subtree("raw_assembler_name", DECL_ASSEMBLER_NAME_RAW(node));
> +    if( DECL_LANG_SPECIFIC(node) )
> +      {
> +      rjd_fprintf("symtab_node(pointer): %p\n",DECL_LANG_SPECIFIC(node));
> +      }
> +    strcpy(ach, "");
> +    if( code == VAR_DECL )
> +      {
> +      if( DECL_DEFER_OUTPUT(node) )
> +        {
> +        strcat(ach, " defer_output");
> +        }
> +      if( DECL_HARD_REGISTER(node) )
> +        {
> +        strcat(ach, " hard_register");
> +        }
> +      if( DECL_COMMON(node) )
> +        {
> +        strcat(ach, " common");
> +        }
> +      if( DECL_IN_TEXT_SECTION(node) )
> +        {
> +        strcat(ach, " in_text_section");
> +        }
> +      if( DECL_IN_CONSTANT_POOL(node) )
> +        {
> +        strcat(ach, " in_constant_pool");
> +        }
> +      if( DECL_DLLIMPORT_P(node) )
> +        {
> +        strcat(ach, " dllimport_flag");
> +        }
> +      }
> +    if( DECL_WEAK(node) )
> +      {
> +      strcat(ach, " weak_flag");
> +      }
> +    if( DECL_SEEN_IN_BIND_EXPR_P(node) )
> +      {
> +      strcat(ach, " seen_in_bind_expr");
> +      }
> +    if( DECL_COMDAT(node) )
> +      {
> +      strcat(ach, " comdat_flag");
> +      }
> +    if( DECL_VISIBILITY_SPECIFIED(node) )
> +      {
> +      strcat(ach," visibility_specified");
> +      }
> +    if( code == VAR_DECL && DECL_HAS_INIT_PRIORITY_P(node) )
> +      {
> +      strcat(ach," init_priority_p");
> +      }
> +    if(  code == FUNCTION_DECL && DECL_FINAL_P(node) )
> +      {
> +      strcat(ach," final");
> +      }
> +    if(  code == FUNCTION_DECL && DECL_STATIC_CHAIN(node) )
> +      {
> +      strcat(ach," regdecl_flag");
> +      }
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("decl_with_vis flags:%s\n",ach);
> +      json_flags("decl_with_vis", ach);
> +      }
> +
> +    const char *p = "???";
> +    switch( DECL_VISIBILITY(node) )
> +      {
> +      case VISIBILITY_DEFAULT:
> +        p = "default";
> +        break;
> +      case VISIBILITY_PROTECTED:
> +        p = "protected";
> +        break;
> +      case VISIBILITY_HIDDEN:
> +        p = "hidden";
> +        break;
> +      case VISIBILITY_INTERNAL:
> +        p = "internal";
> +        break;
> +      }
> +    rjd_fprintf("visibility: %s\n", p);
> +    json_namevalue("visibility", p);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_NON_COMMON) )
> +    {
> +    processed[TS_DECL_NON_COMMON] = 1;
> +    rjd_subtree("result", DECL_RESULT_FLD(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_FUNCTION_DECL) )
> +    {
> +    processed[TS_FUNCTION_DECL] = 1;
> +    if( node->function_decl.f )
> +      {
> +      rjd_fprintf("function(pointer): %p\n",node->function_decl.f);
> +      }
> +
> +    rjd_subtree("arguments", DECL_ARGUMENTS(node));
> +    rjd_subtree("personality", DECL_FUNCTION_PERSONALITY(node));
> +    rjd_subtree("function_specific_target",
> +                              DECL_FUNCTION_SPECIFIC_TARGET(node));
> +    rjd_subtree("function_specific_optimization",
> +                              DECL_FUNCTION_SPECIFIC_OPTIMIZATION(node));
> +    const char *p = "saved_tree";
> +    if( code == FUNCTION_DECL )
> +      {
> +      p = "saved_tree(function_body)";
> +      }
> +    rjd_subtree(p, DECL_SAVED_TREE(node));
> +    rjd_subtree("vindex", DECL_VINDEX(node));
> +
> +    rjd_fprintf("function_code: %d\n",node->function_decl.function_code);
> +
> +    switch(DECL_BUILT_IN_CLASS(node))
> +      {
> +      case NOT_BUILT_IN:
> +        break;
> +      case BUILT_IN_FRONTEND:
> +        rjd_fprintf("built_in: frontend\n");
> +        json_namevalue("built_in", "frontend");
> +        break;
> +      case BUILT_IN_MD:
> +        rjd_fprintf("built_in: md\n");
> +        json_namevalue("built_in", "md");
> +        break;
> +      case BUILT_IN_NORMAL:
> +        rjd_fprintf("built_in: normal\n");
> +        json_namevalue("built_in", "normal");
> +        break;
> +      }
> +    switch(FUNCTION_DECL_DECL_TYPE(node))
> +      {
> +      case NONE:
> +        break;
> +      case OPERATOR_NEW:
> +        rjd_fprintf("operator_new: 1\n");
> +        json_namevalue("operator_new", "1", NOT_QUOTED);
> +        break;
> +      case OPERATOR_DELETE:
> +        rjd_fprintf("operator_delete: 1\n");
> +        json_namevalue("operator_delete", "1", NOT_QUOTED);
> +        break;
> +      case LAMBDA_FUNCTION:
> +        rjd_fprintf("lambda_function: 1\n");
> +        json_namevalue("lambda_function", "1", NOT_QUOTED);
> +        break;
> +      }
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_PUBLIC, "public");
> +    ADD_FLAG(DECL_STATIC_CONSTRUCTOR, "static_ctor_flag");
> +    ADD_FLAG(DECL_STATIC_DESTRUCTOR, "static_dtor_flag");
> +    ADD_FLAG(DECL_UNINLINABLE, "uninlinable");
> +    ADD_FLAG(DECL_POSSIBLY_INLINED, "possibly_inlined");
> +    ADD_FLAG(DECL_IS_NOVOPS, "novops_flag");
> +    ADD_FLAG(DECL_IS_RETURNS_TWICE, "returns_twice_flag");
> +    ADD_FLAG(DECL_IS_MALLOC, "malloc_flag");
> +    ADD_FLAG(DECL_DECLARED_INLINE_P, "declared_inline_flag");
> +    ADD_FLAG(DECL_NO_INLINE_WARNING_P, "no_inline_warning_flag");
> +    ADD_FLAG(DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT,
> +             "no_instrument_function_entry_exit");
> +    ADD_FLAG(DECL_NO_LIMIT_STACK, "no_limit_stack");
> +    if( TREE_CODE(node) == FUNCTION_DECL )
> +      {
> +      ADD_FLAG(DECL_DISREGARD_INLINE_LIMITS, "disregard_inline_limits");
> +      }
> +    ADD_FLAG(DECL_PURE_P, "pure_flag");
> +    ADD_FLAG(DECL_LOOPING_CONST_OR_PURE_P, "looping_const_or_pure_flag");
> +    ADD_FLAG(DECL_HAS_DEBUG_ARGS_P, "has_debug_args_flag");
> +    ADD_FLAG(DECL_FUNCTION_VERSIONED, "versioned_function");
> +    ADD_FLAG(DECL_IS_REPLACEABLE_OPERATOR, "replaceable_operator");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("function_flags:%s\n", ach);
> +      json_flags("function_flags", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_COMMON) )
> +    {
> +    processed[TS_TYPE_COMMON] = 1;
> +
> +    rjd_subtree("size(in bits)", TYPE_SIZE(node));
> +    rjd_subtree("size_unit(in bytes)", TYPE_SIZE_UNIT(node));
> +    rjd_subtree("attributes", TYPE_ATTRIBUTES(node));
> +
> +    sprintf(ach, "%d", TYPE_UID(node));
> +    rjd_fprintf("uid: %s\n", ach);
> +    json_namevalue("uid", ach, NOT_QUOTED);
> +
> +    if( !VECTOR_TYPE_P(node) )
> +      {
> +      sprintf(ach, "%d", TYPE_PRECISION(node));
> +      rjd_fprintf("precision: %s\n", ach);
> +      json_namevalue("precision", ach, NOT_QUOTED);
> +      }
> +
> +    sprintf(ach, "%d", TYPE_CONTAINS_PLACEHOLDER_INTERNAL(node));
> +    rjd_fprintf("contains_placeholder: %d\n", ach);
> +    json_namevalue("contains_placeholder", ach, NOT_QUOTED);
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(TYPE_NO_FORCE_BLK,"no_force_blk_flag");
> +    ADD_FLAG(TYPE_NEEDS_CONSTRUCTING,"needs_constructing_flag");
> +    if( code == RECORD_TYPE
> +        || code == UNION_TYPE
> +        || code == QUAL_UNION_TYPE )
> +      {
> +      ADD_FLAG(TYPE_TRANSPARENT_AGGR,"transparent_aggr_flag");
> +      }
> +    ADD_FLAG(TYPE_RESTRICT, "restrict_flag");
> +    if( code == RECORD_TYPE
> +        || code == UNION_TYPE
> +        || code == QUAL_UNION_TYPE
> +        || code == ARRAY_TYPE )
> +      {
> +      ADD_FLAG(TYPE_TYPELESS_STORAGE, "typeless_storage");
> +      }
> +    ADD_FLAG(TYPE_EMPTY_P, "empty_flag");
> +    ADD_FLAG(TYPE_INDIVISIBLE_P, "indivisible_p");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("type_common_flags:%s\n", ach);
> +      json_flags("type_common_flags", ach);
> +      }
> +
> +    sprintf(ach, "%d",  TYPE_ALIGN(node));
> +    rjd_fprintf("align: %s\n", ach);
> +    json_namevalue("align", ach);
> +
> +    sprintf(ach, "%d", TYPE_WARN_IF_NOT_ALIGN(node));
> +    rjd_fprintf("warn_if_not_align: %s\n", ach);
> +    json_namevalue("warn_if_not_align", ach);
> +
> +    sprintf(ach, "%d", TYPE_ALIAS_SET(node));
> +    rjd_fprintf("alias_set_type: %s\n", ach);
> +    json_namevalue("alias_set_type", ach);
> +
> +    rjd_subtree("pointer_to", TYPE_POINTER_TO(node));
> +    rjd_subtree("reference_to", TYPE_REFERENCE_TO(node));
> +
> +    rjd_subtree("canonical", TYPE_CANONICAL(node));
> +    rjd_subtree("next_variant", TYPE_NEXT_VARIANT(node));
> +    rjd_subtree("main_variant", TYPE_MAIN_VARIANT(node));
> +    rjd_subtree("context", TYPE_CONTEXT(node));
> +    rjd_subtree("name", TYPE_REFERENCE_TO(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_NON_COMMON) )
> +    {
> +    processed[TS_TYPE_NON_COMMON] = 1;
> +    rjd_subtree("values", TYPE_VALUES_RAW(node));
> +    rjd_subtree("minval", TYPE_MIN_VALUE_RAW(node));
> +    rjd_subtree("maxval", TYPE_MAX_VALUE_RAW(node));
> +    rjd_subtree("lang_1", TYPE_LANG_SLOT_1(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_WITH_LANG_SPECIFIC) )
> +    {
> +    processed[TS_TYPE_WITH_LANG_SPECIFIC] = 1;
> +    if( TYPE_LANG_SPECIFIC(node) )
> +      {
> +      rjd_fprintf("lang_type(pointer): %p\n",TYPE_LANG_SPECIFIC(node));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_INT_CST) )
> +    {
> +    processed[TS_INT_CST] = 1;
> +    if( phase == 2 )
> +    rjd_fprintf("value: ");
> +    print_dec(wi::to_wide(node), ach, TYPE_SIGN(TREE_TYPE(node)));
> +    rjd_fprintf("%s" , ach);
> +    rjd_fprintf("\n");
> +    json_namevalue("value", ach, NOT_QUOTED);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_REAL_CST) )
> +    {
> +    bool not_quoted = false;
> +    processed[TS_REAL_CST] = 1;
> +
> +    if(TREE_OVERFLOW(node))
> +      {
> +      strcpy(ach, " overflow ");
> +      }
> +    else
> +      {
> +      REAL_VALUE_TYPE d = TREE_REAL_CST(node);
> +      if(REAL_VALUE_ISINF(d))
> +        {
> +        strcpy(ach, REAL_VALUE_NEGATIVE(d) ? " -Inf" : " Inf");
> +        }
> +      else if(REAL_VALUE_ISNAN(d))
> +        {
> +        /* Print a NaN in the format [-][Q]NaN[(significand[exponent])]
> +         where significand is a hexadecimal string that starts with
> +         the 0x prefix followed by 0 if the number is not canonical
> +         and a non-zero digit if it is, and exponent is decimal.  */
> +        sprintf(ach,
> +                "%s%sNaN",
> +                d.sign ? "-" : "",
> +                d.signalling ? "S" : "Q");
> +        }
> +      else
> +        {
> +        real_to_decimal(ach, &d, sizeof(ach), 0, 1);
> +        not_quoted = true;
> +        }
> +      }
> +
> +    rjd_fprintf("value: %s\n", ach);
> +    json_namevalue("value", ach, not_quoted);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_VEC) )
> +    {
> +    processed[TS_VEC] = 1;
> +
> +    len = TREE_VEC_LENGTH(node);
> +    if(len)
> +      {
> +      json_start_array("tree_vector_elements");
> +      }
> +    for(i = 0; i < len; i++)
> +      {
> +      if( TREE_VEC_ELT(node, i)  )
> +        {
> +        char temp[32];
> +        sprintf(temp, "tree_vector_element[%d]", i);
> +
> +        rjd_subtree(temp, TREE_VEC_ELT(node, i));
> +        json_comma = ",";
> +        }
> +      }
> +    if(len)
> +      {
> +      json_finish_array();
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_EXP) )
> +    {
> +    processed[TS_EXP] = 1;
> +    xloc = expand_location( EXPR_LOCATION(node) );
> +    if( xloc.file )
> +      {
> +      sprintf(ach, "%s:%d:%d",
> +                  xloc.file,
> +                  xloc.line,
> +                  xloc.column);
> +      rjd_fprintf("expression_location %s\n", ach);
> +      json_namevalue("expression_location", ach);
> +      }
> +    len = TREE_OPERAND_LENGTH(node);
> +
> +    tree node_vars = NULL_TREE;
> +    tree node_body = NULL_TREE;
> +    tree node_block = NULL_TREE;
> +
> +    if( len )
> +      {
> +      json_start_array("operands");
> +      }
> +    for(i = 0; i < len; i++)
> +      {
> +      char temp[32];
> +      sprintf(temp, "operand[%d]", i);
> +
> +      if( code == BIND_EXPR )
> +        {
> +        switch(i)
> +          {
> +          // vars/body/block is the order they appear.  I modify that to
> +          // vars/block/body because that's easier for me to deal with
> when I
> +          // manually graph the connections of the results
> +          case 0:
> +            node_vars = node;
> +            continue;
> +            break;
> +          case 1:
> +            node_body = node;
> +            continue;
> +            break;
> +          case 2:
> +            node_block = node;
> +            continue;
> +            break;
> +          }
> +        }
> +      if( code == COMPONENT_REF )
> +        {
> +        switch(i)
> +          {
> +          case 0:
> +            strcpy(temp,"struct/union");
> +            break;
> +          case 1:
> +            strcpy(temp,"field");
> +            break;
> +          case 2:
> +            strcpy(temp,"offset");
> +            break;
> +          }
> +        }
> +      rjd_subtree(temp, TREE_OPERAND(node, i));
> +      json_comma = ",";
> +      }
> +    if( len )
> +      {
> +      json_finish_array();
> +      }
> +
> +    // Here's where I output the block/vars/body in my preferred order
> +    if(node_block)
> +      {
> +      rjd_subtree("block", TREE_OPERAND(node_block, 2));
> +      }
> +    if(node_vars)
> +      {
> +      rjd_subtree("vars", TREE_OPERAND(node_vars, 0));
> +      }
> +    if(node_body)
> +      {
> +      rjd_subtree("body", TREE_OPERAND(node_body, 1));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_LIST) )
> +    {
> +    processed[TS_LIST] = 1;
> +    rjd_subtree("purpose", TREE_PURPOSE(node));
> +    rjd_subtree("value", TREE_VALUE(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_STATEMENT_LIST) )
> +    {
> +    processed[TS_STATEMENT_LIST] = 1;
> +
> +    // We have a linked list to walk:
> +
> +    int i = 0;
> +    tree_statement_list_node *next = STATEMENT_LIST_HEAD(node);
> +    if( next )
> +      {
> +      json_start_array("statement_list");
> +      }
> +    while( next )
> +      {
> +      sprintf(ach,"statement_list[%d]",i++);
> +      rjd_subtree(ach, next->stmt);
> +
> +      // By rights, this next statement, while not illegal, should
> +      // be regarded as immoral.
> +      next = next->next;
> +      json_comma = ",";
> +      }
> +    if( STATEMENT_LIST_HEAD(node) )
> +      {
> +      json_finish_array();
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_RESULT_DECL) )
> +    {
> +    processed[TS_RESULT_DECL] = 1;
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(DECL_BY_REFERENCE, "by_reference");
> +    ADD_FLAG(DECL_NONSHAREABLE, "nonshareable");
> +    ADD_FLAG(DECL_HAS_VALUE_EXPR_P, "has_value_expr");
> +    ADD_FLAG(SSA_VAR_P, "ssa_name_is_possible");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("result_flags:%s\n",ach);
> +      json_flags("result_flags", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_STRING) )
> +    {
> +    // First, do the text/html string:
> +    strcpy(ach, "");
> +    char ach2[8];
> +    processed[TS_STRING] = 1;
> +    const char *p = TREE_STRING_POINTER(node);
> +    int i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
> +    while(--i >= 0)
> +      {
> +      char ch = *p++;
> +      if(ch >= ' ' && ch < 127)
> +        {
> +        sprintf(ach2, "%c", ch);
> +        }
> +      else
> +        {
> +        sprintf(ach2, "\\%03o", ch & 0xFF);
> +        }
> +      strcat(ach, ach2);
> +      }
> +    rjd_fprintf("string: \"");
> +    rjd_fprintf("%s\"\n", ach);
> +
> +    // Second, do the JSON string:
> +    strcpy(ach, "");
> +    p = TREE_STRING_POINTER(node);
> +    i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
> +    while(--i >= 0)
> +      {
> +      char ch = *p++;
> +      strcpy(ach2, "");
> +      switch( ch )
> +        {
> +        case '\"' :
> +          strcpy(ach2, "\\\"");
> +          break;
> +        case '\\' :
> +          strcpy(ach2, "\\\\");
> +          break;
> +        case '/' :
> +          strcpy(ach2, "/");
> +          break;
> +        case '\b' :
> +          strcpy(ach2, "\\b");
> +          break;
> +        case '\f' :
> +          strcpy(ach2, "\\f");
> +          break;
> +        case '\n' :
> +          strcpy(ach2, "\\n");
> +          break;
> +        case '\r' :
> +          strcpy(ach2, "\\r");
> +          break;
> +        case '\t' :
> +          strcpy(ach2, "\\t");
> +          break;
> +        default:
> +        if(ch >= ' ' && ch < 127)
> +          {
> +          sprintf(ach2, "%c", ch);
> +          }
> +        else
> +          {
> +          sprintf(ach2, "\\u%4.4x", (unsigned int)(ch & 0xFF));
> +          }
> +        }
> +      strcat(ach, ach2);
> +      }
> +    json_namevalue("string", ach);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_IDENTIFIER) )
> +    {
> +    processed[TS_IDENTIFIER] = 1;
> +    rjd_fprintf("identifier: \"%s\"\n", IDENTIFIER_POINTER(node));
> +    json_namevalue("identifier", IDENTIFIER_POINTER(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_BLOCK) )
> +    {
> +    processed[TS_BLOCK] = 1;
> +
> +    sprintf(ach, "%u", BLOCK_NUMBER(node));
> +    rjd_fprintf("block_num: %u\n", ach);
> +    json_namevalue("block_num", ach);
> +
> +    xloc = expand_location(BLOCK_SOURCE_LOCATION(node));
> +    if( xloc.file )
> +      {
> +      rjd_fprintf(ach, "%s:%d:%d",
> +                  xloc.file,
> +                  xloc.line,
> +                  xloc.column);
> +      rjd_fprintf("block_location_start: %s\n", ach);
> +      json_namevalue("block_location_start", ach);
> +      }
> +
> +    xloc = expand_location(BLOCK_SOURCE_END_LOCATION(node));
> +    if( xloc.file )
> +      {
> +      rjd_fprintf(ach, "%s:%d:%d",
> +                  xloc.file,
> +                  xloc.line,
> +                  xloc.column);
> +      rjd_fprintf("block_location_end: %s\n", ach);
> +      json_namevalue("block_location_end", ach);
> +      }
> +
> +    if( BLOCK_DIE(node) )
> +      {
> +      rjd_fprintf("DWARF lexical block(pointer) %p\n", BLOCK_DIE(node));
> +      }
> +
> +    rjd_subtree("vars", BLOCK_VARS(node));
> +
> +    len = BLOCK_NUM_NONLOCALIZED_VARS(node);
> +    if( len )
> +      {
> +      json_start_array("nonlocalized_vars");
> +      }
> +    for(i = 0; i < len; i++)
> +      {
> +      char temp[32];
> +      sprintf(temp, "nonlocalized_var[%d]", i);
> +      rjd_subtree(temp, BLOCK_NONLOCALIZED_VAR(node, i));
> +      json_comma = ",";
> +      }
> +    if( len )
> +      {
> +      json_finish_array();
> +      }
> +
> +    rjd_subtree("subblocks", BLOCK_SUBBLOCKS(node));
> +    rjd_subtree("supercontext", BLOCK_SUPERCONTEXT(node));
> +    rjd_subtree("abstract_origin", BLOCK_ABSTRACT_ORIGIN(node));
> +    rjd_subtree("fragment_origin", BLOCK_FRAGMENT_ORIGIN(node));
> +    rjd_subtree("fragment_chain", BLOCK_FRAGMENT_CHAIN(node));
> +    rjd_subtree("chain", BLOCK_CHAIN(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
> +    {
> +    processed[TS_PARM_DECL] = 1;
> +    // The only attribute unique to TS_PARM_DECL is rtx rtl.
> +    // I might process it, if I knew what it was.  RJD 2021-01-29
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_FIELD_DECL) )
> +    {
> +    processed[TS_FIELD_DECL] = 1;
> +    rjd_subtree("offset", DECL_FIELD_OFFSET(node));
> +    rjd_subtree("bit_field_type", DECL_BIT_FIELD_TYPE(node));
> +    rjd_subtree("qualifier", DECL_QUALIFIER(node));
> +    rjd_subtree("bit_offset", DECL_FIELD_BIT_OFFSET(node));
> +    rjd_subtree("fcontext", DECL_FCONTEXT(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_VAR_DECL) )
> +    {
> +    processed[TS_VAR_DECL] = 1;
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_PUBLIC,"public");
> +    ADD_FLAG(DECL_IN_TEXT_SECTION,"in_text_section");
> +    ADD_FLAG(DECL_IN_CONSTANT_POOL,"in_constant_pool");
> +    ADD_FLAG(DECL_HARD_REGISTER,"hard_register");
> +    ADD_FLAG(DECL_HAS_INIT_PRIORITY_P,"init_priority_p");
> +    ADD_FLAG(DECL_HAS_DEBUG_EXPR_P,"debug_expr_is_from");
> +    ADD_FLAG(VAR_DECL_IS_VIRTUAL_OPERAND,"is_virtual_operand");
> +    ADD_FLAG(DECL_NONLOCAL_FRAME,"non_local_frame_structure");
> +    ADD_FLAG(DECL_NONALIASED,"non_aliased");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("var_decl_flags:%s\n",ach);
> +      json_flags("var_decl_flags", ach);
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_DECL) )
> +    {
> +    processed[TS_TYPE_DECL] = 1;
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_PUBLIC,"public");
> +    ADD_FLAG(TYPE_DECL_SUPPRESS_DEBUG,"suppress_debug");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("type_decl_flags:%s\n",ach);
> +      json_flags("type_decl_flags", ach);
> +      }
> +    rjd_subtree("original_type", DECL_ORIGINAL_TYPE(node));
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_CONST_DECL) )
> +    {
> +    processed[TS_CONST_DECL] = 1;
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_TRANSLATION_UNIT_DECL) )
> +    {
> +    processed[TS_TRANSLATION_UNIT_DECL] = 1;
> +    if( TRANSLATION_UNIT_LANGUAGE(node) )
> +      {
> +      rjd_fprintf("language: %s\n", TRANSLATION_UNIT_LANGUAGE(node));
> +      json_namevalue("language", TRANSLATION_UNIT_LANGUAGE(node));
> +      }
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_LABEL_DECL) )
> +    {
> +    processed[TS_LABEL_DECL] = 1;
> +
> +    strcpy(ach, "");
> +    ADD_FLAG(TREE_ADDRESSABLE,"outside_stack_levels");
> +    ADD_FLAG(FORCED_LABEL,"forced_label");
> +    ADD_FLAG(FALLTHROUGH_LABEL_P,"fallthrough_allowed");
> +    ADD_FLAG(SWITCH_BREAK_LABEL_P,"switch_break");
> +    ADD_FLAG(DECL_NONLOCAL,"nonlocal_permitted");
> +    if( strlen(ach) )
> +      {
> +      rjd_fprintf("label_decl_flags:%s\n",ach);
> +      json_flags("label_decl_flags", ach);
> +      }
> +
> +    rjd_subtree("label_context", DECL_CONTEXT(node));
> +
> +    sprintf(ach, "%d", LABEL_DECL_UID(node));
> +    rjd_fprintf("label_uid: %s\n", ach);
> +    json_namevalue("label_uid", ach);
> +
> +    sprintf(ach, "%d", EH_LANDING_PAD_NR(node));
> +    rjd_fprintf("eh_landing_pad: %s\n", ach);
> +    json_namevalue("eh_landing_pad", ach);
> +    }
> +
> +  if( CODE_CONTAINS_STRUCT(code, TS_CONSTRUCTOR) )
> +    {
> +    processed[TS_CONSTRUCTOR] = 1;
> +
> +    if( CONSTRUCTOR_NO_CLEARING(node) )
> +      {
> +      rjd_fprintf("no_clearing: ON\n");
> +      json_namevalue("no_clearing", "true", NOT_QUOTED);
> +      }
> +    else
> +      {
> +      rjd_fprintf("no_clearing: off\n");
> +      json_namevalue("no_clearing", "false", NOT_QUOTED);
> +      }
> +
> +    char temp[32];
> +
> +    if( CONSTRUCTOR_NELTS(node) )
> +      {
> +      json_start_array("constructor_elts");
> +      }
> +    for(size_t i=0; i<CONSTRUCTOR_NELTS(node); i++)
> +      {
> +      constructor_elt *c_elt = CONSTRUCTOR_ELT(node, i);
> +
> +      sprintf(temp, "constructor_elt[%d].index",(int)i);
> +      rjd_subtree(temp, c_elt->index);
> +
> +      sprintf(temp, "constructor_elt[%d].value",(int)i);
> +      rjd_subtree(temp, c_elt->value);
> +
> +      int node_number_i = find_node_in_nodes(c_elt->index);
> +      sprintf(ach, "{\"constructor\":{\"index_node\":%d,",
> node_number_i);
> +      json_newline();
> +      json_fprintf("%s", ach);
> +      json_comma = ",";
> +
> +      int node_number_v = find_node_in_nodes(c_elt->value);
> +      sprintf(ach, "\"value_node\":%d}}", node_number_v);
> +      json_fprintf("%s", ach);
> +      json_comma = ",";
> +      }
> +    if( CONSTRUCTOR_NELTS(node) )
> +      {
> +      json_finish_array();
> +      }
> +    }
> +
> +  // We put this next statement at the end, because it's nice when CHAIN
> +  // is the last thing we see:
> +  if( CODE_CONTAINS_STRUCT(code, TS_COMMON) )
> +    {
> +    processed[TS_COMMON] = 1;
> +    rjd_subtree("chain", TREE_CHAIN(node));
> +    }
> +
> +  for(int i=0; i<64; i++)
> +    {
> +    if( required[i] && !processed[i])
> +      {
> +      /*  Arriving here means that you have encountered a tree struct
> +          that you don't know how to handle.  This happened to me just
> +          now.  The error message I see is
> +
> +              structure 'parm decl': NOT PROCESSED!!!!
> +
> +          What to do?
> +
> +          Well, the first thing I do is look in gcc/treestruct.def for
> +          "parm decl".  I find that text in this line:
> +
> +              DEFTREESTRUCT(TS_PARM_DECL, "parm decl")
> +
> +          This tells me that the tree struct's enum code is TS_PARM_DECL
> +
> +          Next, I look in gcc/tree-core.h for the text "TS_PARM_DECL". I
> find
> +          it in this line:
> +
> +              struct tree_parm_decl  GTY ((tag("TS_PARM_DECL")))
> parm_decl;
> +
> +          This tells me the structure I need to handle is
> "tree_parm_decl".
> +          The declaration of that structure is found earlier in
> gcc/tree-core.h
> +
> +              struct GTY(()) tree_parm_decl {
> +              struct tree_decl_with_rtl common;
> +              rtx incoming_rtl; };
> +
> +          That structure has a substructure "tree_decl_with_rtl", which
> will
> +          require  its one handler.  The structure has its own specific
> +          attribute "rtx incoming_rtl", which we might, or might not want
> to
> +          use as a source of display information.  But, in any case, we
> do need
> +          to add code up above for handling TS_PARM_DECL structures.
> You'll
> +          find that code in the block starting with
> +
> +              if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
> +                  {...}
> +
> +          For each attribute in the TS_xxx that you are working with,
> there is
> +          an accessor macro in gcc/tree.h for getting at that data.  You
> need
> +          to be using those macros; there are a lot of hints and
> information
> +          about the attribute.  For example, by searching for
> ".incoming_rtl"
> +          -- note the leading dot -- you find this commented entry in
> +          gcc/tree.h:
> +
> +              /_* For PARM_DECL, holds an RTL for the stack slot or
> register
> +                 where the data was actually passed.  *_/
> +              #define DECL_INCOMING_RTL(NODE) \
> +               (PARM_DECL_CHECK(NODE)->parm_decl.incoming_rtl)
> +
> +          We are processing a PARM_DECL node, which means that the macro
> +          invocation:
> +
> +              DECL_INCOMING_RTL(node)
> +
> +          will safely return "incoming_rtl", because the PARM_DECL_CHECK
> macro
> +          won't throw an error.
> +
> +          And that's how this error message is eliminated.
> +          */
> +
> +      rjd_fprintf("structure \'%s\': ",ts_enum_names[i]);
> +      rjd_fprintf("NOT PROCESSED!!!!\n" );
> +      fprintf(stderr, "structure \'%s\': ",ts_enum_names[i]);
> +      fprintf(stderr, "NOT PROCESSED!!!!\n" );
> +      exit(1);
> +      }
> +    }
> +
> +  if( phase==2 && fhtml )
> +    {
> +    fprintf(fhtml, "</p>\n");
> +    }
> +}
> +
> +void
> +dump_generic_nodes(const char *filename, tree root)
> +{
> +  /*  This function is called at the beginning of
> gimplify_function_tree() in
> +   *  gimplify.cc.  The 'root' parameter is supposed to be a func_decl
> node.
> +   *
> +   *  When the flag_dump_generic_nodes flag is true, that means the
> compiler was
> +   *  invoked with -fdump-generic-nodes, and that means we jump into
> action.
> +   *
> +   *  We create two files based on filename.  When filename is NULL, it
> gets
> +   *  replaced with the name of the function as extracted from the
> func_decl
> +   *  node.
> +   *
> +   *  filename.nodes is a text file.  filename.nodes.html has the same
> +   *  information in a hypertext file with in-file links to referenced
> nodes.
> +   *
> +   *  The output is a listing of all of the function's nodes and their
> +   *  attributes.  Here is an example of the first two nodes of a typical
> file:
> +
> +***********************************This is NodeNumber0
> +(0x7f12e13b0d00) NodeNumber0
> +tree_code: function_decl
> +tree_code_class: tcc_declaration
> +base_flags: static public
> +type: NodeNumber1 function_type
> +name: NodeNumber6410 identifier_node "main"
> +context: NodeNumber107 translation_unit_decl "bigger.c"
> +source_location: bigger.c:7:5
> +uid: 3663
> +initial(bindings): NodeNumber6411 block
> +machine_mode: QI(15)
> +align: 8
> +warn_if_not_align: 0
> +pt_uid: 3663
> +raw_assembler_name: NodeNumber6410 identifier_node "main"
> +visibility: default
> +result: NodeNumber6412 result_decl
> +function(pointer): 0x7f12e135d508
> +arguments: NodeNumber6413 parm_decl "argc"
> +saved_tree(function_body): NodeNumber6417 statement_list
> +function_code: 0
> +function_flags: public no_instrument_function_entry_exit
> +***********************************This is NodeNumber1
> +(0x7f12e13b3d20) NodeNumber1
> +tree_code: function_type
> +tree_code_class: tcc_type
> +machine_mode: QI(15)
> +type: NodeNumber2 integer_type
> +address_space:0
> +size(in bits): NodeNumber55 uint128 8
> +size_unit(in bytes): NodeNumber12 uint64 1
> +uid: 1515
> +precision: 0
> +contains_placeholder: 0
> +align: 8
> +warn_if_not_align: 0
> +alias_set_type: -1
> +canonical: NodeNumber1 function_type
> +main_variant: NodeNumber1 function_type
> +values: NodeNumber6408 tree_list
> +***********************************This is NodeNumber3
> +
> +  */
> +
> +  if( flag_dump_generic_nodes )
> +    {
> +    GV_number_of_nodes = 0;
> +    char achFilename[1024];
> +    if( !filename )
> +      {
> +      // If, perchance, the root is a decl, use its
> +      // name field as the root of the output file.  This means
> +      // that each function_decl will result in its output file:
> +      // main.tags, foo.tags, bar.tags, and so on.
> +
> +      if( DECL_P(root) )
> +        {
> +        filename = IDENTIFIER_POINTER(DECL_NAME(root));
> +        }
> +      }
> +
> +    if( filename )
> +      {
> +      // Find the root name:
> +      const char *p1 = strrchr(filename,'/');
> +      if( p1 )
> +        {
> +        p1 += 1;
> +        }
> +      else
> +        {
> +        p1 = filename;
> +        }
> +      strcpy(achFilename,filename);
> +      char *p2 = strrchr(achFilename,'.');
> +      if( p2 )
> +        {
> +        *p2 = '\0';
> +        }
> +      strcat(achFilename,".nodes");
> +      ftext = fopen(achFilename, "w");
> +      if( !ftext )
> +          {
> +          error("Unable to open %s", achFilename);
> +          }
> +      // For reasons I don't understand this setvbuf is necessary.
> Without
> +      // it the vfprintf calls here in this routine interfere with
> +      // printf calls elsewhere.
> +      setvbuf(ftext, NULL, _IONBF, 0);
> +
> +      strcat(achFilename,".html");
> +      fhtml = fopen(achFilename, "w");
> +      if( !ftext )
> +          {
> +          error("Unable to open %s", achFilename);
> +          }
> +      setvbuf(fhtml, NULL, _IONBF, 0);
> +
> +      char title[1024];
> +      strcpy(title, "GENERIC for ");
> +      strcat(title, filename);
> +      strcat(title, "()");
> +
> +      html_boilerplate(title);
> +
> +      strcpy(achFilename,filename);
> +      p2 = strrchr(achFilename,'.');
> +      if( p2 )
> +        {
> +        *p2 = '\0';
> +        }
> +      strcat(achFilename,".json");
> +      fjson = fopen(achFilename, "w");
> +      if( !fjson )
> +          {
> +          error("Unable to open %s", achFilename);
> +          }
> +      // For reasons I don't understand this setvbuf is necessary.
> Without
> +      // it the vfprintf calls here in this routine interfere with
> +      // printf calls elsewhere.
> +      setvbuf(fjson, NULL, _IONBF, 0);
> +      }
> +    else
> +      {
> +      fprintf(  stderr,
> +                "%s(): Needs either a filename or a DECL_NODE\n",
> +                __func__);
> +      gcc_assert(false);
> +      }
> +
> +    if( ftext )
> +      {
> +      phase = 1;
> +      rjd_print_node(root);
> +
> +      phase = 2;
> +
> +      // Here is the JSON "header"
> +      json_level = 0;
> +      json_comma = ",";
> +      json_indent();
> +      json_fprintf("{\n");
> +      json_fprintf("\"version\":\"1.0\"");
> +
> +      json_newline();
> +      json_fprintf("\"node_count\":%d", GV_number_of_nodes);
> +
> +      json_comma = ",";
> +      json_newline();
> +      json_fprintf("\"nodes\":\n");
> +      json_fprintf("[");
> +
> +      json_level += 1;
> +      json_comma = "";
> +      for(int i=0; i<GV_number_of_nodes; i++)
> +        {
> +        json_newline();
> +        json_comma = ",";
> +        json_fprintf("{\n");
> +        json_indent();
> +        json_fprintf("\"node\":%d", i);
> +
> +        rjd_print_node(nodes[i].this_node);
> +
> +        json_fprintf("\n");
> +        json_indent();
> +        json_fprintf("}");
> +        }
> +      json_level -= 1;
> +
> +      // Here is the JSON "trailer, that ties off the header"
> +      json_fprintf("\n");
> +      json_fprintf("]\n");
> +      json_fprintf("}\n");
> +      json_level = 1;
> +
> +      fclose(ftext);
> +      ftext = NULL;
> +
> +      html_boilerplate();
> +      fclose(fhtml);
> +      ftext = NULL;
> +
> +      fclose(fjson);
> +      ftext = NULL;
> +      }
> +    }
> +}
> diff --git a/gcc/dump-generic-nodes.h b/gcc/dump-generic-nodes.h
> new file mode 100644
> index 00000000000..ffe18ce2aa5
> --- /dev/null
> +++ b/gcc/dump-generic-nodes.h
> @@ -0,0 +1,26 @@
> +/* Various declarations for language-independent pretty-print
> subroutines.
> +   Copyright (C) 2002-2024 Free Software Foundation, Inc.
> +   Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_PRINT_GIMPLE_NODES_H
> +#define GCC_PRINT_GIMPLE_NODES_H
> +
> +extern void dump_generic_nodes(const char *file_name, tree fndecl);
> +
> +#endif
> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> index 7f79b3cc7e6..7d3c23bcb80 100644
> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "omp-offload.h"
>  #include "context.h"
>  #include "tree-nested.h"
> +#include "dump-generic-nodes.h"
>
>  /* Hash set of poisoned variables in a bind expr.  */
>  static hash_set<tree> *asan_poisoned_variables = NULL;
> @@ -19232,6 +19233,8 @@ gimplify_function_tree (tree fndecl)
>
>    gcc_assert (!gimple_body (fndecl));
>
> +  dump_generic_nodes(NULL, fndecl);
> +
>    if (DECL_STRUCT_FUNCTION (fndecl))
>      push_cfun (DECL_STRUCT_FUNCTION (fndecl));
>    else
> --
> 2.34.1
>
Richard Biener Feb. 28, 2024, 7:58 a.m. UTC | #3
On Tue, Feb 27, 2024 at 10:20 PM Robert Dubner <rdubner@symas.com> wrote:
>
> Richard,
>
> Thank you very much for your comments.
>
> When I set out to create the capability, I had a "specification" in mind.
>
> I didn't have a clue how to create a GENERIC tree that could be fed to the
> middle end in a way that would successfully result in an executable.  And I
> needed to be able to do that in order to proceed with the project of
> creating a COBOL front end.
>
> So, I came up with the idea of using GCC to compile simple programs, and to
> hook into the compiler to examine the trees fed to the middle end, and to
> display those trees in the human-readable format I needed to understand
> them.  And that's what I did.
>
> My first incarnation generated pure text files, and I used that to get
> going.
>
> After a while I realized that when I used the output file, I was spending a
> lot of time searching through the text files.  And I had the brainstorm!
> Hyperlinks!  HTML files!  We have the technology!  So, I created the .HTML
> files as well.
>
> I found this useful to the point of necessity in order to learn how to
> generate the GENERIC trees.  I believe it would be equally useful to the
> next developer who, for whatever reason, needs to understand, on a "You need
> to learn the alphabet before you can learn how to read" level, what the
> middle end requires from a GENERIC tree generated by a front end.
>
> But I've never used it on a complex program. I've used it only to learn how
> to create the GENERIC nodes for very particular things, and so I would use
> the -fdump-generic-nodes feature on a very simple C program that
> demonstrated, in isolation, the feature I needed.  Once I figured it out, I
> would create front end C routines or macros that used the tree.h/tree.cc
> features to build those GENERIC trees, and then I would move on.
>
> I decided to offer it up here, in order to to learn how to create patches
> and to get
> to know the people and the process, as well as from the desire to share it.
> And instantly I got the "How about a machine-readable format?" comments.
> Which are reasonable.  So, because it wasn't hard, I hacked at the existing
> code to create a JSON output.  (But I remind you that up until now, nobody
> seems to have needed a JSON representation.)
>
> And your observation that the human readable representation could be made
> from the JSON representation is totally accurate.
>
> But that wasn't my specification.  My specification was "A tool so that a
> human being can examine a simple GENERIC tree to learn how it's done."
>
> But it seems to me that we are now moving into the realm of a new
> specification.
>
> Said another way:  To go from "A human readable representation of a simple
> GENERIC tree" to "A machine readable JSON representation of an arbitrarily
> complex GENERIC tree, from which a human readable representation can be
> created" means, in effect, starting over on a different project that I don't
> need.  I already *have* a project that I am working on -- the COBOL front
> end.
>
> The complexity of GENERIC trees is, in my experienced opinion, an obstacle
> for the creation of front ends.  The GCC Internals document has a lot of
> information, but to go from it to a front end is like using the maintenance
> manual for an F16 fighter to try to learn to fly the aircraft.
>
> The program "main(){}" generates a tree with over seventy nodes.  I see no
> way to document why that's true; it's all arbitrary in the sense that "this
> is how GCC works".  -fdump-generic-nodes made it possible for me to figure
> out how those nodes are connected and, thus, how to create a new front end.
> I figure that other developers might find it useful, as well.
>
> I guess I am saying that I am not, at this time, able to work on a whole
> different tool.  I think what I have done so far does something useful that
> doesn't seem to otherwise exist in GCC.
>
> I suppose the question for you is, "Is it useful enough?"
>
> I won't be offended if the answer is "No" and I hope you won't be offended
> by my not having the bandwidth to address your very thoughtful and valid
> observations about how it could be better.

No offense taken - I did realize how useful this was to you (and specifically
the hyper-linking looked even very useful to me!).  I often lament the lack
of domain-specific visualization tools for the various data structures GCC
has - having something for GENERIC would be very welcome.

We have for example ways to dump graphviz .dot format graphs of the CFG
and some other data structures and do that natively, not via JSON indirection.

Incidentially this looks like something fit for a google summer of code project.
Ideally it would hook into print-tree.cc providing an alternate
structured output.
It currently prints in the style

 <function_decl 0x7ffff71bc600 bswap16
    type <function_type 0x7ffff71ba5e8
        type <integer_type 0x7ffff702b540 short unsigned int public unsigned HI
            size <integer_cst 0x7ffff702d108 constant 16>
            unit-size <integer_cst 0x7ffff702d120 constant 2>
            align:16 warn_if_not_align:0 symtab:0 alias-set -1
canonical-type 0x7ffff702b540 precision:16 min <integer_cst
0x7ffff702d138 0> max <integer_cst 0x7ffff702d0f0 65535>>
        QI
        size <integer_cst 0x7ffff702d048 constant 8>
        unit-size <integer_cst 0x7ffff702d060 constant 1>
...

where you can see it follows tree -> tree edges up to some depth
(and avoids repeated expansion).  When debugging that's all I have
and I have to follow edges by matching up the raw addresses printed,
re-dumping those that didn't get expanded.  HTML would be indeed
so much nicer here (and a more complete output).

From a maintainance point I think it's important to have "dump a tree node"
once, so when fields are added or deemed useful for presenting in a dump
you don't have to chase down more than one place.  Maintenance is also
the reason to not simply accept your contribution as-is.

I do hope this eventually gets picked up.  I've added a project idea
to https://gcc.gnu.org/wiki/SummerOfCode
and would be willing to mentor it.

Oh, and I'm looking forward to the actual Cobol work!

Thanks,
Richard.

> -----Original Message-----
> From: Richard Biener <richard.guenther@gmail.com>
> Sent: Tuesday, February 27, 2024 04:11
> To: Robert Dubner <rdubner@symas.com>
> Cc: gcc-patches@gcc.gnu.org
> Subject: Re: [PATCH] developer option: -fdump-generic-nodes; initial
> incorporation
>
> On Thu, Feb 22, 2024 at 5:46 PM Robert Dubner <rdubner@symas.com> wrote:
> >
> > As part of an effort to learn how create a GENERIC tree in order to
> > implement a
> > COBOL front end, I created the dump_generic_nodes(), which accepts a
> > function_decl at the point it is provided to the middle end.  The routine
> > generates three files.  One is ASCII, the second is HTML; they contain the
> > tree
> > in a human-readable form.  The third is JSON.
> >
> > This commit modifies common.opt to accept the -fdump-generic-nodes
> > command-line
> > option, creates the dump-generic-nodes.cc and .h files to implement it,
> > and
> > inserts a call to the dump_generic_nodes() function near the top of
> > gimplify_function_tree() in gcc/gimplify.cc
>
> While I think that's good and probably the best you can do in language
> independent code GCCs 'GENERIC' is inherently frontend specific
> (only GIMPLE is no longer).  The gimplifier you hook into eventually
> relies on the 'gimplify_expr' language hook to deal with frontend specific
> tree codes and there might be auxiliary info in the language-specific
> portions of types and decls (DECL_LANG_SPECIFIC, TYPE_LANG_SPECIFIC).
>
> That's a caveat only, it might mean that in some cases a
> 'dump_generic_to_json' language hook might be a nice thing to have.
> Note this is likely only a "problem" for frontends not having their own
> internal AST representation they lower to GENERIC;  first and foremost
> the C and C++ language frontends - I'm not sure of others here, but
> the presence of
>
> ./ada/gcc-interface/ada-tree.def
> ./c/c-tree.def
> ./cp/cp-tree.def
> ./d/d-tree.def
> ./m2/m2-tree.def
> ./objc/objc-tree.def
>
> suggests that "issue" might be wide-spread.
>
> > This patch has been tested on X86_64-linux-gnu.  I haven't tried to
> > provide
> > testcases for the automated system because 1) I haven't learned how to do
> > that,
> > and 2), I am not sure how to test this feature.  On the one hand, the
> > compiler
> > isn't affected when the switch isn't present; when it is present it seems
> > to
> > work on simple source code.
>
> I think this is a useful feature.  I do wonder whether it makes more sense
> to only implement JSON dumping inside the compiler and leave html
> and text output to postprocessing that.  That avoids diverging information
> detail and should reduce the amount of code to maintain.  Scripts to
> perform analysis/transform of such JSON could be contributed into
> the contrib/ directory.
>
> To follow general filename standards for auxiliary files you should
> prepend the filename(s) you dump to with 'aux_base_name', the
> chance of accidentially trashing users files is then reduced a lot.
>
> It might be that with JSON it's reasonable to output a single file per TU
> only, avoiding issues with say C++ function overloading and possible
> clashes on filenames.
>
> https://gcc.gnu.org/contribute.html#standards lists some bits that
> we expect to help common appearance of code and maintainance.
> For example a lot of your functions have no function-level comment
> documenting them.
>
> > Legal requirements:  The FSF has on file an "employer disclaimer" for me.
> >
> > I am using the "Signed off by" tag in an attempt to cover the legal bases;
> > I
> > trust I will be apprised of anything else that needs to be done.
>
> I think that's OK.
>
> > gcc/ChangeLog:
> >
> >         * developer options: -fdump-generic-nodes initial incorporation
> >
> > Signed-off-by: Robert Dubner <rdubner@symas.com>
> > ---
> >  gcc/Makefile.in           |    3 +-
> >  gcc/common.opt            |    4 +
> >  gcc/dump-generic-nodes.cc | 1958 +++++++++++++++++++++++++++++++++++++
> >  gcc/dump-generic-nodes.h  |   26 +
> >  gcc/gimplify.cc           |    3 +
> >  5 files changed, 1993 insertions(+), 1 deletion(-)
> >  create mode 100644 gcc/dump-generic-nodes.cc
> >  create mode 100644 gcc/dump-generic-nodes.h
> >
> > diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> > index a74761b7ab3..81922b0884c 100644
> > --- a/gcc/Makefile.in
> > +++ b/gcc/Makefile.in
> > @@ -1441,6 +1441,7 @@ OBJS = \
> >         domwalk.o \
> >         double-int.o \
> >         dse.o \
> > +       dump-generic-nodes.o \
> >         dumpfile.o \
> >         dwarf2asm.o \
> >         dwarf2cfi.o \
> > @@ -3857,7 +3858,7 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H)
> > coretypes.h $(TM_H) \
> >    hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h
> > \
> >    lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \
> >    pass-instances.def params.list $(srcdir)/../include/gomp-constants.h \
> > -  $(EXPR_H) $(srcdir)/analyzer/*.h
> > +  $(EXPR_H) $(srcdir)/analyzer/*.h dump-generic-nodes.h
> >
> >  # generate the 'build fragment' b-header-vars
> >  s-header-vars: Makefile
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index 51c4a17da83..751b9b1f0cc 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -1583,6 +1583,10 @@ fdump-passes
> >  Common Var(flag_dump_passes) Init(0)
> >  Dump optimization passes.
> >
> > +fdump-generic-nodes
> > +Common Var(flag_dump_generic_nodes) Init(0)
> > +Dump GENERIC trees for each function in three files: <funcname>.nodes,
> > <funcname>.nodes.html, and <funcname>.json
> > +
> >  fdump-unnumbered
> >  Common Var(flag_dump_unnumbered)
> >  Suppress output of instruction numbers, line number notes and addresses
> > in debugging dumps.
> > diff --git a/gcc/dump-generic-nodes.cc b/gcc/dump-generic-nodes.cc
> > new file mode 100644
> > index 00000000000..d44119116d2
> > --- /dev/null
> > +++ b/gcc/dump-generic-nodes.cc
> > @@ -0,0 +1,1958 @@
> > +/* Prints out a tree of generic/gimple nodes in human readable form, both
> > in
> > +   straight text and in HTML. The entry point is dump_generic_nodes().
> > +
> > +   Copyright(C) 1990-2024 Free Software Foundation, Inc.
> > +
> > +This file is part of GCC.
> > +
> > +GCC is free software; you can redistribute it and/or modify it under
> > +the terms of the GNU General Public License as published by the Free
> > +Software Foundation; either version 3, or(at your option) any later
> > +version.
> > +
> > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> > +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> > +for more details.
> > +
> > +You should have received a copy of the GNU General Public License
> > +along with GCC; see the file COPYING3.  If not see
> > +<http://www.gnu.org/licenses/>.  */
> > +
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +#include "tm.h"
> > +#include "tree.h"
> > +#include "cgraph.h"
> > +#include "diagnostic.h"
> > +#include "varasm.h"
> > +#include "print-rtl.h"
> > +#include "stor-layout.h"
> > +#include "langhooks.h"
> > +#include "tree-iterator.h"
> > +#include "gimple-pretty-print.h"
> > +#include "tree-cfg.h"
> > +#include "dumpfile.h"
> > +
> > +#undef DEFTREESTRUCT
> > +#define DEFTREESTRUCT(VAL, NAME) NAME,
> > +static const char *ts_enum_names[] =
> > +  {
> > +  #include "treestruct.def"
> > +  };
> > +#undef DEFTREESTRUCT
> > +
> > +#define ADD_FLAG(accessor,text)if(accessor(node)){strcat(ach," " text);}
> > +
> > +static FILE *ftext = NULL;
> > +static FILE *fhtml = NULL;
> > +static FILE *fjson = NULL;
> > +
> > +static int json_level = 0;
> > +static const char *json_comma;
> > +static const int spaces_per_indent = 2;
> > +
> > +static void rjd_print_node(tree node);
> > +
> > +static int phase = 1;
> > +
> > +/* Define the hash table of nodes already seen.
> > +   Such nodes are not repeated; brief cross-references are used.  */
> > +
> > +struct TREE
> > +  {
> > +  tree this_node;
> > +  int     seen;
> > +  } ;
> > +
> > +static struct TREE *nodes = NULL;
> > +static int GV_size_of_tree = 0;
> > +static int GV_number_of_nodes = 0;
> > +
> > +static void
> > +json_fprintf(const char *format, ...) __attribute__ ((format (printf, 1,
> > 2)));
> > +
> > +static void
> > +json_fprintf(const char *format, ...)
> > +{
> > +  char ach1[2048];
> > +  if( phase == 2 )
> > +    {
> > +    va_list args;
> > +    va_start(args, format);
> > +    vsnprintf(ach1, sizeof(ach1), format, args);
> > +    va_end(args);
> > +    fprintf(fjson, "%s", ach1);
> > +    }
> > +}
> > +
> > +static void
> > +json_indent()
> > +{
> > +  for(int i=0; i<json_level*spaces_per_indent; i++)
> > +    {
> > +    json_fprintf(" ");
> > +    }
> > +}
> > +
> > +static void
> > +json_newline()
> > +{
> > +  json_fprintf("%s\n", json_comma);
> > +  json_indent();
> > +}
> > +
> > +static int
> > +find_node_in_nodes(tree node)
> > +{
> > +  // This is an O(n^2) abomination.  At a suitable time, replace it with
> > a
> > +  // hashed map!
> > +  int retval = -1;
> > +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> > +  for(int i=0; i<GV_number_of_nodes; i++)
> > +    {
> > +    if( nodes[i].this_node == node )
> > +      {
> > +      retval = i;
> > +      break;
> > +      }
> > +    }
> > +  return retval;
> > +}
> > +
> > +static int
> > +add_node_to_nodes(tree node)
> > +{
> > +  // Assume we know the node isn't there yet:
> > +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> > +  if( GV_number_of_nodes == GV_size_of_tree )
> > +    {
> > +    // We've run out of room.  Double the number of nodes:
> > +    GV_size_of_tree *= 2;
> > +    if( GV_size_of_tree == 0 )
> > +      {
> > +      GV_size_of_tree = 64;
> > +      }
> > +    struct TREE *newnodes =
> > +               (struct TREE *)xmalloc( GV_size_of_tree * sizeof(struct
> > TREE) );
> > +    memcpy(newnodes,nodes, GV_number_of_nodes * sizeof(struct TREE));
> > +    free(nodes);
> > +    nodes = newnodes;
> > +    }
> > +  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
> > +  nodes[GV_number_of_nodes].this_node = node;
> > +  nodes[GV_number_of_nodes].seen = 0;
> > +  GV_number_of_nodes += 1;
> > +  return GV_number_of_nodes-1;
> > +}
> > +
> > +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
> > +
> > +static void
> > +rjd_fprintf(const char *format, ...)
> > +{
> > +  // NOTE: I painfully learned that mixing fprintf() and vfprintf() calls
> > +  // can result in hard-to-find memory allocation bugs.  Probably worth
> > +  // tracking down, but the the problem shows up in the libc6 library
> > +  // routines.
> > +  char ach1[2048];
> > +  char ach2[4196];
> > +  char ach3[4196];
> > +  if( phase == 2 )
> > +    {
> > +    va_list args;
> > +    va_start(args, format);
> > +    vsnprintf(ach1, sizeof(ach1), format, args);
> > +    va_end(args);
> > +
> > +    fprintf(ftext, "%s", ach1);
> > +
> > +    // Copy ach1 to ach2, replacing '\n' with "<br>"
> > +    char *p1 = ach1;
> > +    char *p2 = ach2;
> > +
> > +    while( *p1 )
> > +        {
> > +        if(*p1 == '\n')
> > +          {
> > +          p1 += 1;
> > +          *p2 = '\0';
> > +          strcat(p2, "<br>");
> > +          p2 = ach2 + strlen(ach2);
> > +          }
> > +        else
> > +          {
> > +          *p2++ = *p1++;
> > +          }
> > +        }
> > +    size_t ach1_length = strlen(ach1);
> > +
> > +    // To make the HTML more-or-less readable, if ach1 ends with a
> > newline,
> > +    // make ach2 end with a newline as well.
> > +    if( ach1_length && ach1[ach1_length-1] == '\n' )
> > +      {
> > +      *p2++ = '\n';
> > +      }
> > +    *p2++ = '\0';
> > +
> > +    // We now look for NodeNumberNNN and replace it with
> > +    // <a href="#NodeNumberNNN">NodeNumberNNN</a>
> > +
> > +    char *pleft = strstr(ach2, "NodeNumber");
> > +    if( pleft )
> > +      {
> > +      char *pright = pleft + strlen("NodeNumber");
> > +      while( *pright >= '0' && *pright <= '9' )
> > +        {
> > +        pright += 1;
> > +        }
> > +      memset(ach3, 0, sizeof(ach3));
> > +      memcpy(ach3, ach2, pleft - ach2);
> > +      strcat(ach3, "<a href=\"#" );
> > +      char *p = ach3 + strlen(ach3);
> > +      memcpy(p, pleft, pright-pleft);
> > +      strcat(ach3,"\">");
> > +      p = ach3 + strlen(ach3);
> > +      memcpy(p, pleft, pright-pleft);
> > +      p = ach3 + strlen(ach3);
> > +      strcat(ach3,"</a>");
> > +      p = ach3 + strlen(ach3);
> > +      strcpy(p, pright);
> > +      strcpy(ach2, ach3);
> > +      }
> > +
> > +    fprintf(fhtml, "%s", ach2);
> > +    }
> > +}
> > +
> > +#define NOT_QUOTED false
> > +static void
> > +json_namevalue(const char *name, const char *value, bool quoted = true)
> > +{
> > +  if( phase == 2 )
> > +    {
> > +    json_newline();
> > +    if( quoted )
> > +      {
> > +      json_fprintf("\"%s\":\"%s\"", name, value);
> > +      }
> > +    else
> > +      {
> > +      json_fprintf("\"%s\":%s", name, value);
> > +      }
> > +    }
> > +}
> > +
> > +static void
> > +html_boilerplate(char *title=NULL)
> > +{
> > +  if( fhtml )
> > +    {
> > +    if( title )
> > +      {
> > +      fprintf(fhtml,
> > +              "<!DOCTYPE html>\n"
> > +              "<html lang=\"en\">\n"
> > +              "  <head>\n"
> > +              "    <meta charset=\"utf-8\">\n"
> > +              "    <title>%s</title>\n"
> > +              "    <style>\n"
> > +              "    body\n"
> > +              "        {\n"
> > +              "        font-family: courier, serif;\n"
> > +              "        font-size: 13px;\n"
> > +              "        }\n"
> > +              "    </style>\n"
> > +              "  </head>\n"
> > +              "  <body>\n", title);
> > +      }
> > +    else
> > +      {
> > +      fprintf(fhtml, "%s",
> > +              "  </body>\n"
> > +              "</html>\n");
> > +      }
> > +    }
> > +}
> > +
> > +/* Print a node in brief fashion*/
> > +
> > +static void
> > +print_name(const char *prefix, tree node)
> > +{
> > +  if( node && TREE_CODE_CLASS(TREE_CODE(node)) == tcc_declaration )
> > +    {
> > +    tree name = DECL_NAME(node);
> > +    if( name )
> > +      {
> > +      rjd_fprintf("%s\"%s\"", prefix, IDENTIFIER_POINTER(name));
> > +      }
> > +    }
> > +}
> > +
> > +static void
> > +rjd_subtree(const char *subtree_name, tree subtree)
> > +{
> > +  if( subtree )
> > +    {
> > +    if( phase == 1 )
> > +      {
> > +      // Make sure the subtree is in the list of nodes.  And, yes, this
> > +      // is a potential headachy recursivy thingummy.  But computers are
> > +      // good at that.
> > +      rjd_print_node(subtree);
> > +
> > +      int subtree_node_number = find_node_in_nodes(subtree);
> > +      if( subtree_node_number == -1 )
> > +        {
> > +        printf("Run in circles, scream and shout!\n");
> > +        exit(1);
> > +        }
> > +
> > +      }
> > +    else if( phase == 2 )
> > +      {
> > +      char ach[512];
> > +      int node_number = find_node_in_nodes(subtree);
> > +      if( node_number >= 0 )
> > +        {
> > +        rjd_fprintf("%s: NodeNumber%d", subtree_name, node_number);
> > +
> > +        // Handle a few subthings that are very common, and useful to
> > see:
> > +        gcc_assert(node_number < GV_number_of_nodes
> > +                   && GV_number_of_nodes <= GV_size_of_tree);
> > +        tree subnode = nodes[node_number].this_node;
> > +        if( subnode )
> > +          {
> > +          enum tree_code subcode = TREE_CODE(subnode);
> > +
> > +          // After the NodeNumber, print the code name.  Unless it is an
> > +          // integer_cst, because we convert that to int32 or uint64
> > later.
> > +          if( subcode != INTEGER_CST )
> > +            {
> > +            rjd_fprintf(" %s", get_tree_code_name(subcode));
> > +            }
> > +
> > +          if( subcode == IDENTIFIER_NODE && IDENTIFIER_POINTER(subnode) )
> > +            {
> > +            rjd_fprintf(" \"%s\"", IDENTIFIER_POINTER(subnode));
> > +            }
> > +
> > +          print_name(" ", subnode);
> > +
> > +          if( subcode == INTEGER_CST )
> > +            {
> > +            tree int_cst_type = TREE_TYPE(subnode);
> > +            tree_code int_cst_type_code = TREE_CODE(int_cst_type);
> > +
> > +            if( int_cst_type_code == POINTER_TYPE )
> > +              {
> > +              rjd_fprintf(" pointer");
> > +              }
> > +            else if( int_cst_type_code == INTEGER_TYPE)
> > +              {
> > +              // The int_cst_type is an integer_type, so...
> > +              tree min_value = TYPE_MIN_VALUE_RAW(int_cst_type);
> > +              print_dec(wi::to_wide(min_value),
> > +                        ach,
> > +                        TYPE_SIGN(TREE_TYPE(min_value)));
> > +              if( strcmp( ach, "0") )
> > +                {
> > +                rjd_fprintf(" int",ach);
> > +                }
> > +              else
> > +                {
> > +                rjd_fprintf(" uint",ach);
> > +                }
> > +
> > +              tree size_in_bits = TYPE_SIZE(int_cst_type);
> > +
> > +              print_dec(wi::to_wide(size_in_bits),
> > +                        ach,
> > +                        TYPE_SIGN(TREE_TYPE(size_in_bits)));
> > +              rjd_fprintf(ach);
> > +
> > +              print_dec(wi::to_wide(subnode),
> > +                        ach, TYPE_SIGN(TREE_TYPE(subnode)));
> > +              }
> > +
> > +            print_dec(wi::to_wide(subnode),
> > +                      ach,
> > +                      TYPE_SIGN(TREE_TYPE(subnode)));
> > +            rjd_fprintf(" %s",ach);
> > +            }
> > +
> > +          if( subcode == DECL_EXPR )
> > +            {
> > +            int len = TREE_OPERAND_LENGTH(subnode);
> > +            if( len )
> > +              {
> > +              print_name(" ", TREE_OPERAND(subnode, 0));
> > +              }
> > +            }
> > +
> > +          if( subcode == CALL_EXPR )
> > +            {
> > +            int len = TREE_OPERAND_LENGTH(subnode);
> > +            if( len > 1)
> > +              {
> > +              tree addr_expression = TREE_OPERAND(subnode, 1);
> > +              int len2 = TREE_OPERAND_LENGTH(addr_expression);
> > +              if( len2 )
> > +                {
> > +                print_name(" ", TREE_OPERAND(addr_expression, 0));
> > +                }
> > +              }
> > +            }
> > +
> > +          if( subcode == ADDR_EXPR  )
> > +            {
> > +            int len = TREE_OPERAND_LENGTH(subnode);
> > +            if( len > 0)
> > +              {
> > +              print_name(" ", TREE_OPERAND(subnode, 0));
> > +              }
> > +            }
> > +
> > +          if( subcode == MODIFY_EXPR )
> > +            {
> > +            int len = TREE_OPERAND_LENGTH(subnode);
> > +            if( len > 0)
> > +              {
> > +              tree target = TREE_OPERAND(subnode, 0);
> > +              tree_code target_code = TREE_CODE(target);
> > +              tree_code_class target_code_class =
> > TREE_CODE_CLASS(target_code);
> > +              if( target_code_class == tcc_declaration )
> > +                {
> > +                print_name(" ", target);
> > +                }
> > +              else if( target_code == COMPONENT_REF
> > +                                      && target_code_class ==
> > tcc_reference )
> > +                {
> > +                int len = TREE_OPERAND_LENGTH(target);
> > +                tree structure = NULL_TREE;
> > +                tree field     = NULL_TREE;
> > +                if( len > 0 )
> > +                  {
> > +                  structure = TREE_OPERAND(target, 0);
> > +                  }
> > +                if( len > 1 )
> > +                  {
> > +                  field = TREE_OPERAND(target, 1);
> > +                  }
> > +                print_name(" ", structure);
> > +                print_name("::", field);
> > +                }
> > +              }
> > +            }
> > +
> > +          rjd_fprintf("\n");
> > +          }
> > +        // In contrast, for the JSON output, we just want the node
> > number:
> > +        // But we need to intervene in the case where the subtree name
> > starts
> > +        // off with "operand[nnn]".  We are putting them in JSON arraysm,
> > so we
> > +        // need to eliminate that name
> > +        if( strstr(subtree_name, "constructor_elt[") != subtree_name )
> > +          {
> > +          // constructors are handled separately because they have both
> > index
> > +          // and value members.  Search for "constructor_elt" to find
> > that code.
> > +          if(    strstr(subtree_name, "operand[") == subtree_name
> > +              || strstr(subtree_name, "tree_vector_element[") ==
> > subtree_name
> > +              || strstr(subtree_name, "statement_list[") == subtree_name
> > +              || strstr(subtree_name, "nonlocalized_var[") ==
> > subtree_name
> > +              )
> > +            {
> > +            // We are elminating the subtree name
> > +            json_newline();
> > +            }
> > +          else
> > +            {
> > +            json_namevalue(subtree_name, "", NOT_QUOTED);
> > +            }
> > +          json_fprintf("{\"node\":%d}", node_number);
> > +          }
> > +        }
> > +      else
> > +        {
> > +        // This can happen as a result of certain compilation
> > +        // errors.   Just ignore them.
> > +        }
> > +      }
> > +    }
> > +  else
> > +    {
> > +    if( strstr(subtree_name, "operand[") == subtree_name )
> > +      {
> > +      json_newline();
> > +      json_fprintf("{\"node\":null}");
> > +      }
> > +
> > +    }
> > +
> > +}
> > +
> > +static void
> > +json_flags(const char *name, const char *ach)
> > +{
> > +  const char *p = ach+1;  // Skip past the initial space
> > +  json_namevalue(name, "",  NOT_QUOTED);
> > +  json_level += 1;
> > +  json_comma = "";
> > +  json_fprintf("\n");
> > +  json_indent();
> > +  json_fprintf("{");
> > +
> > +  while( *p )
> > +    {
> > +    const char *pend = strchr(p, ' ');
> > +    if( !pend )
> > +      {
> > +      pend = p + strlen(p);
> > +      }
> > +    char achName[256];
> > +    char *d = achName;
> > +    while(p < pend )
> > +      {
> > +      *d++ = *p++;
> > +      }
> > +    *d++= '\0';
> > +    if( *p == ' ' )
> > +      {
> > +      p += 1;
> > +      }
> > +    json_namevalue(achName, "true", NOT_QUOTED);
> > +    json_comma = ",";
> > +    }
> > +  json_fprintf("\n");
> > +  json_indent();
> > +  json_fprintf("}");
> > +
> > +  json_level -= 1;
> > +  json_comma = ",";
> > +}
> > +
> > +static void
> > +json_start_array(const char *name)
> > +{
> > +  // We will set up a JSON array of operands
> > +  json_namevalue(name,"", NOT_QUOTED);
> > +  json_fprintf("\n");
> > +  json_level += 1;
> > +  json_indent();
> > +  json_fprintf("[");
> > +  json_comma = "";
> > +}
> > +static void
> > +json_finish_array()
> > +{
> > +  json_fprintf("\n");
> > +  json_indent();
> > +  json_fprintf("]");
> > +  json_level -= 1;
> > +  json_comma = ",";
> > +}
> > +
> > +static void
> > +rjd_print_node(tree node)
> > +{
> > +  char ach[4096];
> > +  enum tree_code_class tclass;
> > +  int len;
> > +  int i;
> > +  expanded_location xloc;
> > +  enum tree_code code;
> > +
> > +  if(node == 0)
> > +    {
> > +    // When handled a NULL, just return
> > +    return;
> > +    }
> > +
> > +  int node_number = find_node_in_nodes(node);
> > +  if( phase == 1 )
> > +    {
> > +    if( node_number != -1 )
> > +      {
> > +      // We're building the list of nodes, and we've already processed
> > this
> > +      // node:
> > +      return;
> > +      }
> > +
> > +    // We are building the list of nodes, and this is a new one:
> > +    node_number = add_node_to_nodes(node);
> > +    }
> > +  // From here on out, we know that node_number is valid, whether in
> > +  // phase 1 or phase 2
> > +
> > +  code = TREE_CODE(node);
> > +
> > +  /* It is unsafe to look at any other fields of a node with ERROR_MARK
> > or
> > +     invalid code.  */
> > +  if(code == ERROR_MARK || code >= MAX_TREE_CODES)
> > +    {
> > +    rjd_fprintf("This node is unsafe.  The reported TREE_CODE is
> > %d\n",code);
> > +    return;
> > +    }
> > +
> > +  tclass = TREE_CODE_CLASS(code);
> > +
> > +  /* Announce the coming of a new node: */
> > +  if( phase==2 && fhtml )
> > +    {
> > +    fprintf(fhtml, "<p id=\"NodeNumber%d\">\n", node_number);
> > +    }
> > +  rjd_fprintf("***********************************This is
> > NodeNumber%d\n",
> > +              node_number);
> > +
> > +  /* Print the NodeNumber in a more canonical form: */
> > +  rjd_fprintf("(%p) NodeNumber%d\n", node, node_number);
> > +
> > +  // Print the tree_code for this node
> > +  rjd_fprintf("tree_code: %s\n", get_tree_code_name(code));
> > +  json_namevalue("tree_code", get_tree_code_name(code));
> > +
> > +  // It might be useful to see the class
> > +  const char *classtxt;
> > +  switch(tclass)
> > +    {
> > +    case tcc_exceptional:
> > +      classtxt = "tcc_exceptional";
> > +      break;
> > +    case tcc_constant:
> > +      classtxt = "tcc_constant";
> > +      break;
> > +    case tcc_type:
> > +      classtxt = "tcc_type";
> > +      break;
> > +    case tcc_declaration:
> > +      classtxt = "tcc_declaration";
> > +      break;
> > +    case tcc_reference:
> > +      classtxt = "tcc_reference";
> > +      break;
> > +    case tcc_comparison:
> > +      classtxt = "tcc_comparison";
> > +      break;
> > +    case tcc_unary:
> > +      classtxt = "tcc_unary";
> > +      break;
> > +    case tcc_binary:
> > +      classtxt = "tcc_binary";
> > +      break;
> > +    case tcc_statement:
> > +      classtxt = "tcc_statement";
> > +      break;
> > +    case tcc_vl_exp:
> > +      classtxt = "tcc_vl_exp";
> > +      break;
> > +    case tcc_expression:
> > +      classtxt = "tcc_expression";
> > +      break;
> > +    default:
> > +      gcc_unreachable();
> > +      break;
> > +    }
> > +  rjd_fprintf("tree_code_class: %s\n", classtxt);
> > +  json_namevalue("tree_code_class", classtxt);
> > +
> > +  int required[64];
> > +  int processed[64];
> > +  for(int i=0; i<64; i++)
> > +    {
> > +    required[i] = CODE_CONTAINS_STRUCT(code, i);
> > +    processed[i] = 0;
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_BASE) )
> > +    {
> > +    processed[TS_BASE] = 1;
> > +    // There are 16 bits in TS_BASE.  The trouble is, they have
> > +    // different meanings for different codes; see the extensive
> > +    // comment in tree-core.h
> > +    strcpy(ach, "");
> > +    if( tclass != tcc_type && TREE_SIDE_EFFECTS(node) )
> > +      {
> > +      strcat(ach," side_effects");
> > +      }
> > +    if( tclass != tcc_type && TREE_CONSTANT(node) )
> > +      {
> > +      strcat(ach," constant");
> > +      }
> > +    if( TREE_ADDRESSABLE(node) )
> > +      {
> > +      strcat(ach," addressable");
> > +      }
> > +    if( TREE_THIS_VOLATILE(node) )
> > +      {
> > +      strcat(ach," volatile");
> > +      }
> > +    if( tclass != tcc_type && TREE_READONLY(node) )
> > +      {
> > +      strcat(ach," readonly");
> > +      }
> > +    if( TREE_ASM_WRITTEN(node) )
> > +      {
> > +      strcat(ach," asm_written");
> > +      }
> > +    if( TREE_NO_WARNING(node) )
> > +      {
> > +      strcat(ach," nowarning");
> > +      }
> > +    if( TREE_VISITED(node) )
> > +      {
> > +      strcat(ach," visited");
> > +      }
> > +
> > +    if( TREE_USED(node) )
> > +      {
> > +      strcat(ach," used");
> > +      }
> > +    if( TREE_NOTHROW(node) )
> > +      {
> > +      strcat(ach," nothrow");
> > +      }
> > +    if( TREE_STATIC(node) )
> > +      {
> > +      strcat(ach," static");
> > +      }
> > +    if( TREE_PUBLIC(node) )
> > +      {
> > +      strcat(ach," public");
> > +      }
> > +    if( TREE_PRIVATE(node) )
> > +      {
> > +      strcat(ach," private");
> > +      }
> > +    if( TREE_PROTECTED(node) )
> > +      {
> > +      strcat(ach," protected");
> > +      }
> > +    if( TREE_DEPRECATED(node) )
> > +      {
> > +      strcat(ach," deprecated");
> > +      }
> > +    if( node->base.default_def_flag ) //There isn't a TREE_DEFAULT_DEF
> > macro
> > +      {
> > +      strcat(ach," default_def");
> > +      }
> > +    if( tclass == tcc_constant )
> > +      {
> > +      if( TREE_OVERFLOW(node) )
> > +        {
> > +        strcat(ach," overflow");
> > +        }
> > +      }
> > +    if( tclass == tcc_type )
> > +      {
> > +      ADD_FLAG(TYPE_UNSIGNED,   "unsigned");
> > +      ADD_FLAG(TYPE_PACKED,     "packed");
> > +      ADD_FLAG(TYPE_USER_ALIGN, "user_align");
> > +      ADD_FLAG(TYPE_NAMELESS,   "nameless");
> > +      ADD_FLAG(TYPE_ATOMIC,     "atomic");
> > +      }
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("base_flags:%s\n", ach);
> > +      json_flags("base_flags", ach);
> > +      }
> > +    }
> > +
> > +  if( tclass == tcc_type )
> > +    {
> > +    sprintf(ach, "%s(%u)",
> > +            GET_MODE_NAME(TYPE_MODE(node)),(int)TYPE_MODE(node));
> > +    rjd_fprintf("machine_mode: %s\n", ach);
> > +    json_namevalue("machine_mode", ach);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_TYPED) )
> > +    {
> > +    processed[TS_TYPED] = 1;
> > +    rjd_subtree("type", TREE_TYPE(node));
> > +    if( tclass == tcc_type )
> > +      {
> > +      sprintf(ach, "%u", (int)TYPE_ADDR_SPACE(node));
> > +      rjd_fprintf("address_space:%s\n", ach);
> > +      json_namevalue("address_space", ach);
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_MINIMAL) )
> > +    {
> > +    processed[TS_DECL_MINIMAL] = 1;
> > +    rjd_subtree("name", DECL_NAME(node));
> > +    rjd_subtree("context", DECL_CONTEXT(node));
> > +    xloc = expand_location(DECL_SOURCE_LOCATION(node));
> > +    if( xloc.file )
> > +      {
> > +      sprintf(ach, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
> > +      rjd_fprintf("source_location: %s\n", ach);
> > +      json_namevalue("source_location", ach);
> > +      }
> > +    sprintf(ach, "%d", DECL_PT_UID(node));
> > +    rjd_fprintf("uid: %s\n", ach);
> > +    json_namevalue("uid", ach, NOT_QUOTED);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_COMMON) )
> > +    {
> > +    processed[TS_DECL_COMMON] = 1;
> > +    rjd_subtree("size(in bits)", DECL_SIZE(node));
> > +    rjd_subtree("size_unit(in bytes)", DECL_SIZE_UNIT(node));
> > +
> > +    const char *p;
> > +    switch(code)
> > +      {
> > +      case FUNCTION_DECL:
> > +        p = "initial(bindings)";
> > +        break;
> > +      case TRANSLATION_UNIT_DECL:
> > +        p = "initial(block)";
> > +        break;
> > +      case VAR_DECL:
> > +        p = "initial value";
> > +        break;
> > +      default:
> > +        p = "initial";
> > +        break;
> > +      }
> > +    rjd_subtree(p, DECL_INITIAL(node));
> > +    rjd_subtree("attributes", DECL_ATTRIBUTES(node));
> > +    rjd_subtree("abstract_origin", DECL_ABSTRACT_ORIGIN(node));
> > +
> > +    sprintf(ach, "%s(%u)",
> > GET_MODE_NAME(DECL_MODE(node)),(int)DECL_MODE(node));
> > +    rjd_fprintf("machine_mode: %s\n", ach);
> > +    json_namevalue("machine_mode", ach);
> > +
> > +    strcpy(ach, "");
> > +    if( DECL_NONLOCAL(node) )
> > +      {
> > +      strcat(ach," nonlocal");
> > +      }
> > +    if( DECL_VIRTUAL_P(node) )
> > +      {
> > +      strcat(ach," virtual");
> > +      }
> > +    if( DECL_IGNORED_P(node) )
> > +      {
> > +      strcat(ach," ignored");
> > +      }
> > +    if( DECL_ABSTRACT_P(node) )
> > +      {
> > +      strcat(ach," abstrac");
> > +      }
> > +    if( DECL_ARTIFICIAL(node) )
> > +      {
> > +      strcat(ach," artificial");
> > +      }
> > +    if( DECL_PRESERVE_P(node) )
> > +      {
> > +      strcat(ach," preserve");
> > +      }
> > +    if( code == VAR_DECL && DECL_DEBUG_EXPR(node) )
> > +      {
> > +      strcat(ach," debug_expr_is_from");
> > +      }
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("decl_flags:%s\n",ach);
> > +      json_flags("decl_flags", ach);
> > +      }
> > +
> > +    if( code == FIELD_DECL )
> > +      {
> > +      sprintf(ach, "%u", (int)DECL_OFFSET_ALIGN(node));
> > +      rjd_fprintf("offset_align: %s\n", ach);
> > +      json_namevalue("offset_align", ach);
> > +      }
> > +    sprintf(ach, "%u", (int)DECL_ALIGN(node));
> > +    rjd_fprintf("align: %u\n", ach);
> > +    json_namevalue("align", ach, NOT_QUOTED);
> > +
> > +    sprintf(ach, "%u", (int)DECL_WARN_IF_NOT_ALIGN(node));
> > +    rjd_fprintf("warn_if_not_align: %s\n", ach);
> > +    json_namevalue("warn_if_not_align", ach, NOT_QUOTED);
> > +
> > +    sprintf(ach, "%u", (int)DECL_PT_UID(node));
> > +    rjd_fprintf("pt_uid: %s\n", ach);
> > +    json_namevalue("pt_uid", ach, NOT_QUOTED);
> > +
> > +    if( DECL_LANG_SPECIFIC(node) )
> > +      {
> > +      rjd_fprintf("lang_specific(pointer):
> > %p\n",DECL_LANG_SPECIFIC(node));
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WRTL) )
> > +    {
> > +    processed[TS_DECL_WRTL] = 1;
> > +    if( DECL_RTL_SET_P(node) )
> > +      {
> > +      rjd_fprintf("DECL_RTL for NODE has already been set\n");
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WITH_VIS) )
> > +    {
> > +    processed[TS_DECL_WITH_VIS] = 1;
> > +    rjd_subtree("raw_assembler_name", DECL_ASSEMBLER_NAME_RAW(node));
> > +    if( DECL_LANG_SPECIFIC(node) )
> > +      {
> > +      rjd_fprintf("symtab_node(pointer): %p\n",DECL_LANG_SPECIFIC(node));
> > +      }
> > +    strcpy(ach, "");
> > +    if( code == VAR_DECL )
> > +      {
> > +      if( DECL_DEFER_OUTPUT(node) )
> > +        {
> > +        strcat(ach, " defer_output");
> > +        }
> > +      if( DECL_HARD_REGISTER(node) )
> > +        {
> > +        strcat(ach, " hard_register");
> > +        }
> > +      if( DECL_COMMON(node) )
> > +        {
> > +        strcat(ach, " common");
> > +        }
> > +      if( DECL_IN_TEXT_SECTION(node) )
> > +        {
> > +        strcat(ach, " in_text_section");
> > +        }
> > +      if( DECL_IN_CONSTANT_POOL(node) )
> > +        {
> > +        strcat(ach, " in_constant_pool");
> > +        }
> > +      if( DECL_DLLIMPORT_P(node) )
> > +        {
> > +        strcat(ach, " dllimport_flag");
> > +        }
> > +      }
> > +    if( DECL_WEAK(node) )
> > +      {
> > +      strcat(ach, " weak_flag");
> > +      }
> > +    if( DECL_SEEN_IN_BIND_EXPR_P(node) )
> > +      {
> > +      strcat(ach, " seen_in_bind_expr");
> > +      }
> > +    if( DECL_COMDAT(node) )
> > +      {
> > +      strcat(ach, " comdat_flag");
> > +      }
> > +    if( DECL_VISIBILITY_SPECIFIED(node) )
> > +      {
> > +      strcat(ach," visibility_specified");
> > +      }
> > +    if( code == VAR_DECL && DECL_HAS_INIT_PRIORITY_P(node) )
> > +      {
> > +      strcat(ach," init_priority_p");
> > +      }
> > +    if(  code == FUNCTION_DECL && DECL_FINAL_P(node) )
> > +      {
> > +      strcat(ach," final");
> > +      }
> > +    if(  code == FUNCTION_DECL && DECL_STATIC_CHAIN(node) )
> > +      {
> > +      strcat(ach," regdecl_flag");
> > +      }
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("decl_with_vis flags:%s\n",ach);
> > +      json_flags("decl_with_vis", ach);
> > +      }
> > +
> > +    const char *p = "???";
> > +    switch( DECL_VISIBILITY(node) )
> > +      {
> > +      case VISIBILITY_DEFAULT:
> > +        p = "default";
> > +        break;
> > +      case VISIBILITY_PROTECTED:
> > +        p = "protected";
> > +        break;
> > +      case VISIBILITY_HIDDEN:
> > +        p = "hidden";
> > +        break;
> > +      case VISIBILITY_INTERNAL:
> > +        p = "internal";
> > +        break;
> > +      }
> > +    rjd_fprintf("visibility: %s\n", p);
> > +    json_namevalue("visibility", p);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_DECL_NON_COMMON) )
> > +    {
> > +    processed[TS_DECL_NON_COMMON] = 1;
> > +    rjd_subtree("result", DECL_RESULT_FLD(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_FUNCTION_DECL) )
> > +    {
> > +    processed[TS_FUNCTION_DECL] = 1;
> > +    if( node->function_decl.f )
> > +      {
> > +      rjd_fprintf("function(pointer): %p\n",node->function_decl.f);
> > +      }
> > +
> > +    rjd_subtree("arguments", DECL_ARGUMENTS(node));
> > +    rjd_subtree("personality", DECL_FUNCTION_PERSONALITY(node));
> > +    rjd_subtree("function_specific_target",
> > +                              DECL_FUNCTION_SPECIFIC_TARGET(node));
> > +    rjd_subtree("function_specific_optimization",
> > +                              DECL_FUNCTION_SPECIFIC_OPTIMIZATION(node));
> > +    const char *p = "saved_tree";
> > +    if( code == FUNCTION_DECL )
> > +      {
> > +      p = "saved_tree(function_body)";
> > +      }
> > +    rjd_subtree(p, DECL_SAVED_TREE(node));
> > +    rjd_subtree("vindex", DECL_VINDEX(node));
> > +
> > +    rjd_fprintf("function_code: %d\n",node->function_decl.function_code);
> > +
> > +    switch(DECL_BUILT_IN_CLASS(node))
> > +      {
> > +      case NOT_BUILT_IN:
> > +        break;
> > +      case BUILT_IN_FRONTEND:
> > +        rjd_fprintf("built_in: frontend\n");
> > +        json_namevalue("built_in", "frontend");
> > +        break;
> > +      case BUILT_IN_MD:
> > +        rjd_fprintf("built_in: md\n");
> > +        json_namevalue("built_in", "md");
> > +        break;
> > +      case BUILT_IN_NORMAL:
> > +        rjd_fprintf("built_in: normal\n");
> > +        json_namevalue("built_in", "normal");
> > +        break;
> > +      }
> > +    switch(FUNCTION_DECL_DECL_TYPE(node))
> > +      {
> > +      case NONE:
> > +        break;
> > +      case OPERATOR_NEW:
> > +        rjd_fprintf("operator_new: 1\n");
> > +        json_namevalue("operator_new", "1", NOT_QUOTED);
> > +        break;
> > +      case OPERATOR_DELETE:
> > +        rjd_fprintf("operator_delete: 1\n");
> > +        json_namevalue("operator_delete", "1", NOT_QUOTED);
> > +        break;
> > +      case LAMBDA_FUNCTION:
> > +        rjd_fprintf("lambda_function: 1\n");
> > +        json_namevalue("lambda_function", "1", NOT_QUOTED);
> > +        break;
> > +      }
> > +
> > +    strcpy(ach, "");
> > +    ADD_FLAG(TREE_PUBLIC, "public");
> > +    ADD_FLAG(DECL_STATIC_CONSTRUCTOR, "static_ctor_flag");
> > +    ADD_FLAG(DECL_STATIC_DESTRUCTOR, "static_dtor_flag");
> > +    ADD_FLAG(DECL_UNINLINABLE, "uninlinable");
> > +    ADD_FLAG(DECL_POSSIBLY_INLINED, "possibly_inlined");
> > +    ADD_FLAG(DECL_IS_NOVOPS, "novops_flag");
> > +    ADD_FLAG(DECL_IS_RETURNS_TWICE, "returns_twice_flag");
> > +    ADD_FLAG(DECL_IS_MALLOC, "malloc_flag");
> > +    ADD_FLAG(DECL_DECLARED_INLINE_P, "declared_inline_flag");
> > +    ADD_FLAG(DECL_NO_INLINE_WARNING_P, "no_inline_warning_flag");
> > +    ADD_FLAG(DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT,
> > +             "no_instrument_function_entry_exit");
> > +    ADD_FLAG(DECL_NO_LIMIT_STACK, "no_limit_stack");
> > +    if( TREE_CODE(node) == FUNCTION_DECL )
> > +      {
> > +      ADD_FLAG(DECL_DISREGARD_INLINE_LIMITS, "disregard_inline_limits");
> > +      }
> > +    ADD_FLAG(DECL_PURE_P, "pure_flag");
> > +    ADD_FLAG(DECL_LOOPING_CONST_OR_PURE_P, "looping_const_or_pure_flag");
> > +    ADD_FLAG(DECL_HAS_DEBUG_ARGS_P, "has_debug_args_flag");
> > +    ADD_FLAG(DECL_FUNCTION_VERSIONED, "versioned_function");
> > +    ADD_FLAG(DECL_IS_REPLACEABLE_OPERATOR, "replaceable_operator");
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("function_flags:%s\n", ach);
> > +      json_flags("function_flags", ach);
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_COMMON) )
> > +    {
> > +    processed[TS_TYPE_COMMON] = 1;
> > +
> > +    rjd_subtree("size(in bits)", TYPE_SIZE(node));
> > +    rjd_subtree("size_unit(in bytes)", TYPE_SIZE_UNIT(node));
> > +    rjd_subtree("attributes", TYPE_ATTRIBUTES(node));
> > +
> > +    sprintf(ach, "%d", TYPE_UID(node));
> > +    rjd_fprintf("uid: %s\n", ach);
> > +    json_namevalue("uid", ach, NOT_QUOTED);
> > +
> > +    if( !VECTOR_TYPE_P(node) )
> > +      {
> > +      sprintf(ach, "%d", TYPE_PRECISION(node));
> > +      rjd_fprintf("precision: %s\n", ach);
> > +      json_namevalue("precision", ach, NOT_QUOTED);
> > +      }
> > +
> > +    sprintf(ach, "%d", TYPE_CONTAINS_PLACEHOLDER_INTERNAL(node));
> > +    rjd_fprintf("contains_placeholder: %d\n", ach);
> > +    json_namevalue("contains_placeholder", ach, NOT_QUOTED);
> > +
> > +    strcpy(ach, "");
> > +    ADD_FLAG(TYPE_NO_FORCE_BLK,"no_force_blk_flag");
> > +    ADD_FLAG(TYPE_NEEDS_CONSTRUCTING,"needs_constructing_flag");
> > +    if( code == RECORD_TYPE
> > +        || code == UNION_TYPE
> > +        || code == QUAL_UNION_TYPE )
> > +      {
> > +      ADD_FLAG(TYPE_TRANSPARENT_AGGR,"transparent_aggr_flag");
> > +      }
> > +    ADD_FLAG(TYPE_RESTRICT, "restrict_flag");
> > +    if( code == RECORD_TYPE
> > +        || code == UNION_TYPE
> > +        || code == QUAL_UNION_TYPE
> > +        || code == ARRAY_TYPE )
> > +      {
> > +      ADD_FLAG(TYPE_TYPELESS_STORAGE, "typeless_storage");
> > +      }
> > +    ADD_FLAG(TYPE_EMPTY_P, "empty_flag");
> > +    ADD_FLAG(TYPE_INDIVISIBLE_P, "indivisible_p");
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("type_common_flags:%s\n", ach);
> > +      json_flags("type_common_flags", ach);
> > +      }
> > +
> > +    sprintf(ach, "%d",  TYPE_ALIGN(node));
> > +    rjd_fprintf("align: %s\n", ach);
> > +    json_namevalue("align", ach);
> > +
> > +    sprintf(ach, "%d", TYPE_WARN_IF_NOT_ALIGN(node));
> > +    rjd_fprintf("warn_if_not_align: %s\n", ach);
> > +    json_namevalue("warn_if_not_align", ach);
> > +
> > +    sprintf(ach, "%d", TYPE_ALIAS_SET(node));
> > +    rjd_fprintf("alias_set_type: %s\n", ach);
> > +    json_namevalue("alias_set_type", ach);
> > +
> > +    rjd_subtree("pointer_to", TYPE_POINTER_TO(node));
> > +    rjd_subtree("reference_to", TYPE_REFERENCE_TO(node));
> > +
> > +    rjd_subtree("canonical", TYPE_CANONICAL(node));
> > +    rjd_subtree("next_variant", TYPE_NEXT_VARIANT(node));
> > +    rjd_subtree("main_variant", TYPE_MAIN_VARIANT(node));
> > +    rjd_subtree("context", TYPE_CONTEXT(node));
> > +    rjd_subtree("name", TYPE_REFERENCE_TO(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_NON_COMMON) )
> > +    {
> > +    processed[TS_TYPE_NON_COMMON] = 1;
> > +    rjd_subtree("values", TYPE_VALUES_RAW(node));
> > +    rjd_subtree("minval", TYPE_MIN_VALUE_RAW(node));
> > +    rjd_subtree("maxval", TYPE_MAX_VALUE_RAW(node));
> > +    rjd_subtree("lang_1", TYPE_LANG_SLOT_1(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_WITH_LANG_SPECIFIC) )
> > +    {
> > +    processed[TS_TYPE_WITH_LANG_SPECIFIC] = 1;
> > +    if( TYPE_LANG_SPECIFIC(node) )
> > +      {
> > +      rjd_fprintf("lang_type(pointer): %p\n",TYPE_LANG_SPECIFIC(node));
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_INT_CST) )
> > +    {
> > +    processed[TS_INT_CST] = 1;
> > +    if( phase == 2 )
> > +    rjd_fprintf("value: ");
> > +    print_dec(wi::to_wide(node), ach, TYPE_SIGN(TREE_TYPE(node)));
> > +    rjd_fprintf("%s" , ach);
> > +    rjd_fprintf("\n");
> > +    json_namevalue("value", ach, NOT_QUOTED);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_REAL_CST) )
> > +    {
> > +    bool not_quoted = false;
> > +    processed[TS_REAL_CST] = 1;
> > +
> > +    if(TREE_OVERFLOW(node))
> > +      {
> > +      strcpy(ach, " overflow ");
> > +      }
> > +    else
> > +      {
> > +      REAL_VALUE_TYPE d = TREE_REAL_CST(node);
> > +      if(REAL_VALUE_ISINF(d))
> > +        {
> > +        strcpy(ach, REAL_VALUE_NEGATIVE(d) ? " -Inf" : " Inf");
> > +        }
> > +      else if(REAL_VALUE_ISNAN(d))
> > +        {
> > +        /* Print a NaN in the format [-][Q]NaN[(significand[exponent])]
> > +         where significand is a hexadecimal string that starts with
> > +         the 0x prefix followed by 0 if the number is not canonical
> > +         and a non-zero digit if it is, and exponent is decimal.  */
> > +        sprintf(ach,
> > +                "%s%sNaN",
> > +                d.sign ? "-" : "",
> > +                d.signalling ? "S" : "Q");
> > +        }
> > +      else
> > +        {
> > +        real_to_decimal(ach, &d, sizeof(ach), 0, 1);
> > +        not_quoted = true;
> > +        }
> > +      }
> > +
> > +    rjd_fprintf("value: %s\n", ach);
> > +    json_namevalue("value", ach, not_quoted);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_VEC) )
> > +    {
> > +    processed[TS_VEC] = 1;
> > +
> > +    len = TREE_VEC_LENGTH(node);
> > +    if(len)
> > +      {
> > +      json_start_array("tree_vector_elements");
> > +      }
> > +    for(i = 0; i < len; i++)
> > +      {
> > +      if( TREE_VEC_ELT(node, i)  )
> > +        {
> > +        char temp[32];
> > +        sprintf(temp, "tree_vector_element[%d]", i);
> > +
> > +        rjd_subtree(temp, TREE_VEC_ELT(node, i));
> > +        json_comma = ",";
> > +        }
> > +      }
> > +    if(len)
> > +      {
> > +      json_finish_array();
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_EXP) )
> > +    {
> > +    processed[TS_EXP] = 1;
> > +    xloc = expand_location( EXPR_LOCATION(node) );
> > +    if( xloc.file )
> > +      {
> > +      sprintf(ach, "%s:%d:%d",
> > +                  xloc.file,
> > +                  xloc.line,
> > +                  xloc.column);
> > +      rjd_fprintf("expression_location %s\n", ach);
> > +      json_namevalue("expression_location", ach);
> > +      }
> > +    len = TREE_OPERAND_LENGTH(node);
> > +
> > +    tree node_vars = NULL_TREE;
> > +    tree node_body = NULL_TREE;
> > +    tree node_block = NULL_TREE;
> > +
> > +    if( len )
> > +      {
> > +      json_start_array("operands");
> > +      }
> > +    for(i = 0; i < len; i++)
> > +      {
> > +      char temp[32];
> > +      sprintf(temp, "operand[%d]", i);
> > +
> > +      if( code == BIND_EXPR )
> > +        {
> > +        switch(i)
> > +          {
> > +          // vars/body/block is the order they appear.  I modify that to
> > +          // vars/block/body because that's easier for me to deal with
> > when I
> > +          // manually graph the connections of the results
> > +          case 0:
> > +            node_vars = node;
> > +            continue;
> > +            break;
> > +          case 1:
> > +            node_body = node;
> > +            continue;
> > +            break;
> > +          case 2:
> > +            node_block = node;
> > +            continue;
> > +            break;
> > +          }
> > +        }
> > +      if( code == COMPONENT_REF )
> > +        {
> > +        switch(i)
> > +          {
> > +          case 0:
> > +            strcpy(temp,"struct/union");
> > +            break;
> > +          case 1:
> > +            strcpy(temp,"field");
> > +            break;
> > +          case 2:
> > +            strcpy(temp,"offset");
> > +            break;
> > +          }
> > +        }
> > +      rjd_subtree(temp, TREE_OPERAND(node, i));
> > +      json_comma = ",";
> > +      }
> > +    if( len )
> > +      {
> > +      json_finish_array();
> > +      }
> > +
> > +    // Here's where I output the block/vars/body in my preferred order
> > +    if(node_block)
> > +      {
> > +      rjd_subtree("block", TREE_OPERAND(node_block, 2));
> > +      }
> > +    if(node_vars)
> > +      {
> > +      rjd_subtree("vars", TREE_OPERAND(node_vars, 0));
> > +      }
> > +    if(node_body)
> > +      {
> > +      rjd_subtree("body", TREE_OPERAND(node_body, 1));
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_LIST) )
> > +    {
> > +    processed[TS_LIST] = 1;
> > +    rjd_subtree("purpose", TREE_PURPOSE(node));
> > +    rjd_subtree("value", TREE_VALUE(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_STATEMENT_LIST) )
> > +    {
> > +    processed[TS_STATEMENT_LIST] = 1;
> > +
> > +    // We have a linked list to walk:
> > +
> > +    int i = 0;
> > +    tree_statement_list_node *next = STATEMENT_LIST_HEAD(node);
> > +    if( next )
> > +      {
> > +      json_start_array("statement_list");
> > +      }
> > +    while( next )
> > +      {
> > +      sprintf(ach,"statement_list[%d]",i++);
> > +      rjd_subtree(ach, next->stmt);
> > +
> > +      // By rights, this next statement, while not illegal, should
> > +      // be regarded as immoral.
> > +      next = next->next;
> > +      json_comma = ",";
> > +      }
> > +    if( STATEMENT_LIST_HEAD(node) )
> > +      {
> > +      json_finish_array();
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_RESULT_DECL) )
> > +    {
> > +    processed[TS_RESULT_DECL] = 1;
> > +
> > +    strcpy(ach, "");
> > +    ADD_FLAG(DECL_BY_REFERENCE, "by_reference");
> > +    ADD_FLAG(DECL_NONSHAREABLE, "nonshareable");
> > +    ADD_FLAG(DECL_HAS_VALUE_EXPR_P, "has_value_expr");
> > +    ADD_FLAG(SSA_VAR_P, "ssa_name_is_possible");
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("result_flags:%s\n",ach);
> > +      json_flags("result_flags", ach);
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_STRING) )
> > +    {
> > +    // First, do the text/html string:
> > +    strcpy(ach, "");
> > +    char ach2[8];
> > +    processed[TS_STRING] = 1;
> > +    const char *p = TREE_STRING_POINTER(node);
> > +    int i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
> > +    while(--i >= 0)
> > +      {
> > +      char ch = *p++;
> > +      if(ch >= ' ' && ch < 127)
> > +        {
> > +        sprintf(ach2, "%c", ch);
> > +        }
> > +      else
> > +        {
> > +        sprintf(ach2, "\\%03o", ch & 0xFF);
> > +        }
> > +      strcat(ach, ach2);
> > +      }
> > +    rjd_fprintf("string: \"");
> > +    rjd_fprintf("%s\"\n", ach);
> > +
> > +    // Second, do the JSON string:
> > +    strcpy(ach, "");
> > +    p = TREE_STRING_POINTER(node);
> > +    i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
> > +    while(--i >= 0)
> > +      {
> > +      char ch = *p++;
> > +      strcpy(ach2, "");
> > +      switch( ch )
> > +        {
> > +        case '\"' :
> > +          strcpy(ach2, "\\\"");
> > +          break;
> > +        case '\\' :
> > +          strcpy(ach2, "\\\\");
> > +          break;
> > +        case '/' :
> > +          strcpy(ach2, "/");
> > +          break;
> > +        case '\b' :
> > +          strcpy(ach2, "\\b");
> > +          break;
> > +        case '\f' :
> > +          strcpy(ach2, "\\f");
> > +          break;
> > +        case '\n' :
> > +          strcpy(ach2, "\\n");
> > +          break;
> > +        case '\r' :
> > +          strcpy(ach2, "\\r");
> > +          break;
> > +        case '\t' :
> > +          strcpy(ach2, "\\t");
> > +          break;
> > +        default:
> > +        if(ch >= ' ' && ch < 127)
> > +          {
> > +          sprintf(ach2, "%c", ch);
> > +          }
> > +        else
> > +          {
> > +          sprintf(ach2, "\\u%4.4x", (unsigned int)(ch & 0xFF));
> > +          }
> > +        }
> > +      strcat(ach, ach2);
> > +      }
> > +    json_namevalue("string", ach);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_IDENTIFIER) )
> > +    {
> > +    processed[TS_IDENTIFIER] = 1;
> > +    rjd_fprintf("identifier: \"%s\"\n", IDENTIFIER_POINTER(node));
> > +    json_namevalue("identifier", IDENTIFIER_POINTER(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_BLOCK) )
> > +    {
> > +    processed[TS_BLOCK] = 1;
> > +
> > +    sprintf(ach, "%u", BLOCK_NUMBER(node));
> > +    rjd_fprintf("block_num: %u\n", ach);
> > +    json_namevalue("block_num", ach);
> > +
> > +    xloc = expand_location(BLOCK_SOURCE_LOCATION(node));
> > +    if( xloc.file )
> > +      {
> > +      rjd_fprintf(ach, "%s:%d:%d",
> > +                  xloc.file,
> > +                  xloc.line,
> > +                  xloc.column);
> > +      rjd_fprintf("block_location_start: %s\n", ach);
> > +      json_namevalue("block_location_start", ach);
> > +      }
> > +
> > +    xloc = expand_location(BLOCK_SOURCE_END_LOCATION(node));
> > +    if( xloc.file )
> > +      {
> > +      rjd_fprintf(ach, "%s:%d:%d",
> > +                  xloc.file,
> > +                  xloc.line,
> > +                  xloc.column);
> > +      rjd_fprintf("block_location_end: %s\n", ach);
> > +      json_namevalue("block_location_end", ach);
> > +      }
> > +
> > +    if( BLOCK_DIE(node) )
> > +      {
> > +      rjd_fprintf("DWARF lexical block(pointer) %p\n", BLOCK_DIE(node));
> > +      }
> > +
> > +    rjd_subtree("vars", BLOCK_VARS(node));
> > +
> > +    len = BLOCK_NUM_NONLOCALIZED_VARS(node);
> > +    if( len )
> > +      {
> > +      json_start_array("nonlocalized_vars");
> > +      }
> > +    for(i = 0; i < len; i++)
> > +      {
> > +      char temp[32];
> > +      sprintf(temp, "nonlocalized_var[%d]", i);
> > +      rjd_subtree(temp, BLOCK_NONLOCALIZED_VAR(node, i));
> > +      json_comma = ",";
> > +      }
> > +    if( len )
> > +      {
> > +      json_finish_array();
> > +      }
> > +
> > +    rjd_subtree("subblocks", BLOCK_SUBBLOCKS(node));
> > +    rjd_subtree("supercontext", BLOCK_SUPERCONTEXT(node));
> > +    rjd_subtree("abstract_origin", BLOCK_ABSTRACT_ORIGIN(node));
> > +    rjd_subtree("fragment_origin", BLOCK_FRAGMENT_ORIGIN(node));
> > +    rjd_subtree("fragment_chain", BLOCK_FRAGMENT_CHAIN(node));
> > +    rjd_subtree("chain", BLOCK_CHAIN(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
> > +    {
> > +    processed[TS_PARM_DECL] = 1;
> > +    // The only attribute unique to TS_PARM_DECL is rtx rtl.
> > +    // I might process it, if I knew what it was.  RJD 2021-01-29
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_FIELD_DECL) )
> > +    {
> > +    processed[TS_FIELD_DECL] = 1;
> > +    rjd_subtree("offset", DECL_FIELD_OFFSET(node));
> > +    rjd_subtree("bit_field_type", DECL_BIT_FIELD_TYPE(node));
> > +    rjd_subtree("qualifier", DECL_QUALIFIER(node));
> > +    rjd_subtree("bit_offset", DECL_FIELD_BIT_OFFSET(node));
> > +    rjd_subtree("fcontext", DECL_FCONTEXT(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_VAR_DECL) )
> > +    {
> > +    processed[TS_VAR_DECL] = 1;
> > +    strcpy(ach, "");
> > +    ADD_FLAG(TREE_PUBLIC,"public");
> > +    ADD_FLAG(DECL_IN_TEXT_SECTION,"in_text_section");
> > +    ADD_FLAG(DECL_IN_CONSTANT_POOL,"in_constant_pool");
> > +    ADD_FLAG(DECL_HARD_REGISTER,"hard_register");
> > +    ADD_FLAG(DECL_HAS_INIT_PRIORITY_P,"init_priority_p");
> > +    ADD_FLAG(DECL_HAS_DEBUG_EXPR_P,"debug_expr_is_from");
> > +    ADD_FLAG(VAR_DECL_IS_VIRTUAL_OPERAND,"is_virtual_operand");
> > +    ADD_FLAG(DECL_NONLOCAL_FRAME,"non_local_frame_structure");
> > +    ADD_FLAG(DECL_NONALIASED,"non_aliased");
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("var_decl_flags:%s\n",ach);
> > +      json_flags("var_decl_flags", ach);
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_DECL) )
> > +    {
> > +    processed[TS_TYPE_DECL] = 1;
> > +    strcpy(ach, "");
> > +    ADD_FLAG(TREE_PUBLIC,"public");
> > +    ADD_FLAG(TYPE_DECL_SUPPRESS_DEBUG,"suppress_debug");
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("type_decl_flags:%s\n",ach);
> > +      json_flags("type_decl_flags", ach);
> > +      }
> > +    rjd_subtree("original_type", DECL_ORIGINAL_TYPE(node));
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_CONST_DECL) )
> > +    {
> > +    processed[TS_CONST_DECL] = 1;
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_TRANSLATION_UNIT_DECL) )
> > +    {
> > +    processed[TS_TRANSLATION_UNIT_DECL] = 1;
> > +    if( TRANSLATION_UNIT_LANGUAGE(node) )
> > +      {
> > +      rjd_fprintf("language: %s\n", TRANSLATION_UNIT_LANGUAGE(node));
> > +      json_namevalue("language", TRANSLATION_UNIT_LANGUAGE(node));
> > +      }
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_LABEL_DECL) )
> > +    {
> > +    processed[TS_LABEL_DECL] = 1;
> > +
> > +    strcpy(ach, "");
> > +    ADD_FLAG(TREE_ADDRESSABLE,"outside_stack_levels");
> > +    ADD_FLAG(FORCED_LABEL,"forced_label");
> > +    ADD_FLAG(FALLTHROUGH_LABEL_P,"fallthrough_allowed");
> > +    ADD_FLAG(SWITCH_BREAK_LABEL_P,"switch_break");
> > +    ADD_FLAG(DECL_NONLOCAL,"nonlocal_permitted");
> > +    if( strlen(ach) )
> > +      {
> > +      rjd_fprintf("label_decl_flags:%s\n",ach);
> > +      json_flags("label_decl_flags", ach);
> > +      }
> > +
> > +    rjd_subtree("label_context", DECL_CONTEXT(node));
> > +
> > +    sprintf(ach, "%d", LABEL_DECL_UID(node));
> > +    rjd_fprintf("label_uid: %s\n", ach);
> > +    json_namevalue("label_uid", ach);
> > +
> > +    sprintf(ach, "%d", EH_LANDING_PAD_NR(node));
> > +    rjd_fprintf("eh_landing_pad: %s\n", ach);
> > +    json_namevalue("eh_landing_pad", ach);
> > +    }
> > +
> > +  if( CODE_CONTAINS_STRUCT(code, TS_CONSTRUCTOR) )
> > +    {
> > +    processed[TS_CONSTRUCTOR] = 1;
> > +
> > +    if( CONSTRUCTOR_NO_CLEARING(node) )
> > +      {
> > +      rjd_fprintf("no_clearing: ON\n");
> > +      json_namevalue("no_clearing", "true", NOT_QUOTED);
> > +      }
> > +    else
> > +      {
> > +      rjd_fprintf("no_clearing: off\n");
> > +      json_namevalue("no_clearing", "false", NOT_QUOTED);
> > +      }
> > +
> > +    char temp[32];
> > +
> > +    if( CONSTRUCTOR_NELTS(node) )
> > +      {
> > +      json_start_array("constructor_elts");
> > +      }
> > +    for(size_t i=0; i<CONSTRUCTOR_NELTS(node); i++)
> > +      {
> > +      constructor_elt *c_elt = CONSTRUCTOR_ELT(node, i);
> > +
> > +      sprintf(temp, "constructor_elt[%d].index",(int)i);
> > +      rjd_subtree(temp, c_elt->index);
> > +
> > +      sprintf(temp, "constructor_elt[%d].value",(int)i);
> > +      rjd_subtree(temp, c_elt->value);
> > +
> > +      int node_number_i = find_node_in_nodes(c_elt->index);
> > +      sprintf(ach, "{\"constructor\":{\"index_node\":%d,",
> > node_number_i);
> > +      json_newline();
> > +      json_fprintf("%s", ach);
> > +      json_comma = ",";
> > +
> > +      int node_number_v = find_node_in_nodes(c_elt->value);
> > +      sprintf(ach, "\"value_node\":%d}}", node_number_v);
> > +      json_fprintf("%s", ach);
> > +      json_comma = ",";
> > +      }
> > +    if( CONSTRUCTOR_NELTS(node) )
> > +      {
> > +      json_finish_array();
> > +      }
> > +    }
> > +
> > +  // We put this next statement at the end, because it's nice when CHAIN
> > +  // is the last thing we see:
> > +  if( CODE_CONTAINS_STRUCT(code, TS_COMMON) )
> > +    {
> > +    processed[TS_COMMON] = 1;
> > +    rjd_subtree("chain", TREE_CHAIN(node));
> > +    }
> > +
> > +  for(int i=0; i<64; i++)
> > +    {
> > +    if( required[i] && !processed[i])
> > +      {
> > +      /*  Arriving here means that you have encountered a tree struct
> > +          that you don't know how to handle.  This happened to me just
> > +          now.  The error message I see is
> > +
> > +              structure 'parm decl': NOT PROCESSED!!!!
> > +
> > +          What to do?
> > +
> > +          Well, the first thing I do is look in gcc/treestruct.def for
> > +          "parm decl".  I find that text in this line:
> > +
> > +              DEFTREESTRUCT(TS_PARM_DECL, "parm decl")
> > +
> > +          This tells me that the tree struct's enum code is TS_PARM_DECL
> > +
> > +          Next, I look in gcc/tree-core.h for the text "TS_PARM_DECL". I
> > find
> > +          it in this line:
> > +
> > +              struct tree_parm_decl  GTY ((tag("TS_PARM_DECL")))
> > parm_decl;
> > +
> > +          This tells me the structure I need to handle is
> > "tree_parm_decl".
> > +          The declaration of that structure is found earlier in
> > gcc/tree-core.h
> > +
> > +              struct GTY(()) tree_parm_decl {
> > +              struct tree_decl_with_rtl common;
> > +              rtx incoming_rtl; };
> > +
> > +          That structure has a substructure "tree_decl_with_rtl", which
> > will
> > +          require  its one handler.  The structure has its own specific
> > +          attribute "rtx incoming_rtl", which we might, or might not want
> > to
> > +          use as a source of display information.  But, in any case, we
> > do need
> > +          to add code up above for handling TS_PARM_DECL structures.
> > You'll
> > +          find that code in the block starting with
> > +
> > +              if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
> > +                  {...}
> > +
> > +          For each attribute in the TS_xxx that you are working with,
> > there is
> > +          an accessor macro in gcc/tree.h for getting at that data.  You
> > need
> > +          to be using those macros; there are a lot of hints and
> > information
> > +          about the attribute.  For example, by searching for
> > ".incoming_rtl"
> > +          -- note the leading dot -- you find this commented entry in
> > +          gcc/tree.h:
> > +
> > +              /_* For PARM_DECL, holds an RTL for the stack slot or
> > register
> > +                 where the data was actually passed.  *_/
> > +              #define DECL_INCOMING_RTL(NODE) \
> > +               (PARM_DECL_CHECK(NODE)->parm_decl.incoming_rtl)
> > +
> > +          We are processing a PARM_DECL node, which means that the macro
> > +          invocation:
> > +
> > +              DECL_INCOMING_RTL(node)
> > +
> > +          will safely return "incoming_rtl", because the PARM_DECL_CHECK
> > macro
> > +          won't throw an error.
> > +
> > +          And that's how this error message is eliminated.
> > +          */
> > +
> > +      rjd_fprintf("structure \'%s\': ",ts_enum_names[i]);
> > +      rjd_fprintf("NOT PROCESSED!!!!\n" );
> > +      fprintf(stderr, "structure \'%s\': ",ts_enum_names[i]);
> > +      fprintf(stderr, "NOT PROCESSED!!!!\n" );
> > +      exit(1);
> > +      }
> > +    }
> > +
> > +  if( phase==2 && fhtml )
> > +    {
> > +    fprintf(fhtml, "</p>\n");
> > +    }
> > +}
> > +
> > +void
> > +dump_generic_nodes(const char *filename, tree root)
> > +{
> > +  /*  This function is called at the beginning of
> > gimplify_function_tree() in
> > +   *  gimplify.cc.  The 'root' parameter is supposed to be a func_decl
> > node.
> > +   *
> > +   *  When the flag_dump_generic_nodes flag is true, that means the
> > compiler was
> > +   *  invoked with -fdump-generic-nodes, and that means we jump into
> > action.
> > +   *
> > +   *  We create two files based on filename.  When filename is NULL, it
> > gets
> > +   *  replaced with the name of the function as extracted from the
> > func_decl
> > +   *  node.
> > +   *
> > +   *  filename.nodes is a text file.  filename.nodes.html has the same
> > +   *  information in a hypertext file with in-file links to referenced
> > nodes.
> > +   *
> > +   *  The output is a listing of all of the function's nodes and their
> > +   *  attributes.  Here is an example of the first two nodes of a typical
> > file:
> > +
> > +***********************************This is NodeNumber0
> > +(0x7f12e13b0d00) NodeNumber0
> > +tree_code: function_decl
> > +tree_code_class: tcc_declaration
> > +base_flags: static public
> > +type: NodeNumber1 function_type
> > +name: NodeNumber6410 identifier_node "main"
> > +context: NodeNumber107 translation_unit_decl "bigger.c"
> > +source_location: bigger.c:7:5
> > +uid: 3663
> > +initial(bindings): NodeNumber6411 block
> > +machine_mode: QI(15)
> > +align: 8
> > +warn_if_not_align: 0
> > +pt_uid: 3663
> > +raw_assembler_name: NodeNumber6410 identifier_node "main"
> > +visibility: default
> > +result: NodeNumber6412 result_decl
> > +function(pointer): 0x7f12e135d508
> > +arguments: NodeNumber6413 parm_decl "argc"
> > +saved_tree(function_body): NodeNumber6417 statement_list
> > +function_code: 0
> > +function_flags: public no_instrument_function_entry_exit
> > +***********************************This is NodeNumber1
> > +(0x7f12e13b3d20) NodeNumber1
> > +tree_code: function_type
> > +tree_code_class: tcc_type
> > +machine_mode: QI(15)
> > +type: NodeNumber2 integer_type
> > +address_space:0
> > +size(in bits): NodeNumber55 uint128 8
> > +size_unit(in bytes): NodeNumber12 uint64 1
> > +uid: 1515
> > +precision: 0
> > +contains_placeholder: 0
> > +align: 8
> > +warn_if_not_align: 0
> > +alias_set_type: -1
> > +canonical: NodeNumber1 function_type
> > +main_variant: NodeNumber1 function_type
> > +values: NodeNumber6408 tree_list
> > +***********************************This is NodeNumber3
> > +
> > +  */
> > +
> > +  if( flag_dump_generic_nodes )
> > +    {
> > +    GV_number_of_nodes = 0;
> > +    char achFilename[1024];
> > +    if( !filename )
> > +      {
> > +      // If, perchance, the root is a decl, use its
> > +      // name field as the root of the output file.  This means
> > +      // that each function_decl will result in its output file:
> > +      // main.tags, foo.tags, bar.tags, and so on.
> > +
> > +      if( DECL_P(root) )
> > +        {
> > +        filename = IDENTIFIER_POINTER(DECL_NAME(root));
> > +        }
> > +      }
> > +
> > +    if( filename )
> > +      {
> > +      // Find the root name:
> > +      const char *p1 = strrchr(filename,'/');
> > +      if( p1 )
> > +        {
> > +        p1 += 1;
> > +        }
> > +      else
> > +        {
> > +        p1 = filename;
> > +        }
> > +      strcpy(achFilename,filename);
> > +      char *p2 = strrchr(achFilename,'.');
> > +      if( p2 )
> > +        {
> > +        *p2 = '\0';
> > +        }
> > +      strcat(achFilename,".nodes");
> > +      ftext = fopen(achFilename, "w");
> > +      if( !ftext )
> > +          {
> > +          error("Unable to open %s", achFilename);
> > +          }
> > +      // For reasons I don't understand this setvbuf is necessary.
> > Without
> > +      // it the vfprintf calls here in this routine interfere with
> > +      // printf calls elsewhere.
> > +      setvbuf(ftext, NULL, _IONBF, 0);
> > +
> > +      strcat(achFilename,".html");
> > +      fhtml = fopen(achFilename, "w");
> > +      if( !ftext )
> > +          {
> > +          error("Unable to open %s", achFilename);
> > +          }
> > +      setvbuf(fhtml, NULL, _IONBF, 0);
> > +
> > +      char title[1024];
> > +      strcpy(title, "GENERIC for ");
> > +      strcat(title, filename);
> > +      strcat(title, "()");
> > +
> > +      html_boilerplate(title);
> > +
> > +      strcpy(achFilename,filename);
> > +      p2 = strrchr(achFilename,'.');
> > +      if( p2 )
> > +        {
> > +        *p2 = '\0';
> > +        }
> > +      strcat(achFilename,".json");
> > +      fjson = fopen(achFilename, "w");
> > +      if( !fjson )
> > +          {
> > +          error("Unable to open %s", achFilename);
> > +          }
> > +      // For reasons I don't understand this setvbuf is necessary.
> > Without
> > +      // it the vfprintf calls here in this routine interfere with
> > +      // printf calls elsewhere.
> > +      setvbuf(fjson, NULL, _IONBF, 0);
> > +      }
> > +    else
> > +      {
> > +      fprintf(  stderr,
> > +                "%s(): Needs either a filename or a DECL_NODE\n",
> > +                __func__);
> > +      gcc_assert(false);
> > +      }
> > +
> > +    if( ftext )
> > +      {
> > +      phase = 1;
> > +      rjd_print_node(root);
> > +
> > +      phase = 2;
> > +
> > +      // Here is the JSON "header"
> > +      json_level = 0;
> > +      json_comma = ",";
> > +      json_indent();
> > +      json_fprintf("{\n");
> > +      json_fprintf("\"version\":\"1.0\"");
> > +
> > +      json_newline();
> > +      json_fprintf("\"node_count\":%d", GV_number_of_nodes);
> > +
> > +      json_comma = ",";
> > +      json_newline();
> > +      json_fprintf("\"nodes\":\n");
> > +      json_fprintf("[");
> > +
> > +      json_level += 1;
> > +      json_comma = "";
> > +      for(int i=0; i<GV_number_of_nodes; i++)
> > +        {
> > +        json_newline();
> > +        json_comma = ",";
> > +        json_fprintf("{\n");
> > +        json_indent();
> > +        json_fprintf("\"node\":%d", i);
> > +
> > +        rjd_print_node(nodes[i].this_node);
> > +
> > +        json_fprintf("\n");
> > +        json_indent();
> > +        json_fprintf("}");
> > +        }
> > +      json_level -= 1;
> > +
> > +      // Here is the JSON "trailer, that ties off the header"
> > +      json_fprintf("\n");
> > +      json_fprintf("]\n");
> > +      json_fprintf("}\n");
> > +      json_level = 1;
> > +
> > +      fclose(ftext);
> > +      ftext = NULL;
> > +
> > +      html_boilerplate();
> > +      fclose(fhtml);
> > +      ftext = NULL;
> > +
> > +      fclose(fjson);
> > +      ftext = NULL;
> > +      }
> > +    }
> > +}
> > diff --git a/gcc/dump-generic-nodes.h b/gcc/dump-generic-nodes.h
> > new file mode 100644
> > index 00000000000..ffe18ce2aa5
> > --- /dev/null
> > +++ b/gcc/dump-generic-nodes.h
> > @@ -0,0 +1,26 @@
> > +/* Various declarations for language-independent pretty-print
> > subroutines.
> > +   Copyright (C) 2002-2024 Free Software Foundation, Inc.
> > +   Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
> > +
> > +This file is part of GCC.
> > +
> > +GCC is free software; you can redistribute it and/or modify it under
> > +the terms of the GNU General Public License as published by the Free
> > +Software Foundation; either version 3, or (at your option) any later
> > +version.
> > +
> > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> > +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> > +for more details.
> > +
> > +You should have received a copy of the GNU General Public License
> > +along with GCC; see the file COPYING3.  If not see
> > +<http://www.gnu.org/licenses/>.  */
> > +
> > +#ifndef GCC_PRINT_GIMPLE_NODES_H
> > +#define GCC_PRINT_GIMPLE_NODES_H
> > +
> > +extern void dump_generic_nodes(const char *file_name, tree fndecl);
> > +
> > +#endif
> > diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> > index 7f79b3cc7e6..7d3c23bcb80 100644
> > --- a/gcc/gimplify.cc
> > +++ b/gcc/gimplify.cc
> > @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
> >  #include "omp-offload.h"
> >  #include "context.h"
> >  #include "tree-nested.h"
> > +#include "dump-generic-nodes.h"
> >
> >  /* Hash set of poisoned variables in a bind expr.  */
> >  static hash_set<tree> *asan_poisoned_variables = NULL;
> > @@ -19232,6 +19233,8 @@ gimplify_function_tree (tree fndecl)
> >
> >    gcc_assert (!gimple_body (fndecl));
> >
> > +  dump_generic_nodes(NULL, fndecl);
> > +
> >    if (DECL_STRUCT_FUNCTION (fndecl))
> >      push_cfun (DECL_STRUCT_FUNCTION (fndecl));
> >    else
> > --
> > 2.34.1
> >
Jakub Jelinek Feb. 28, 2024, 8:25 a.m. UTC | #4
On Wed, Feb 28, 2024 at 08:58:08AM +0100, Richard Biener wrote:
> Incidentially this looks like something fit for a google summer of code project.
> Ideally it would hook into print-tree.cc providing an alternate
> structured output.
> It currently prints in the style
> 
>  <function_decl 0x7ffff71bc600 bswap16
>     type <function_type 0x7ffff71ba5e8
>         type <integer_type 0x7ffff702b540 short unsigned int public unsigned HI
>             size <integer_cst 0x7ffff702d108 constant 16>
>             unit-size <integer_cst 0x7ffff702d120 constant 2>
>             align:16 warn_if_not_align:0 symtab:0 alias-set -1
> canonical-type 0x7ffff702b540 precision:16 min <integer_cst
> 0x7ffff702d138 0> max <integer_cst 0x7ffff702d0f0 65535>>
>         QI
>         size <integer_cst 0x7ffff702d048 constant 8>
>         unit-size <integer_cst 0x7ffff702d060 constant 1>
> ...
> 
> where you can see it follows tree -> tree edges up to some depth
> (and avoids repeated expansion).  When debugging that's all I have
> and I have to follow edges by matching up the raw addresses printed,
> re-dumping those that didn't get expanded.  HTML would be indeed
> so much nicer here (and a more complete output).

I think keeping the current format of what is printed but optionally just
turn all those addresses in there into hyperlinks which would expand the
other trees would be nice.  Maybe also allow just hovering on the link and
show the other tree printed might be nice too.
Folding it all into just <function_decl ... bswap <type link> ...>
would mean one can't quickly access just the min/max or fn return type
etc.  We might need some parameter how deep to go (and/or how many trees to
dump at most) so that we don't dump into HTML gigabytes of data when asking
to print say a large BIND_EXPR into HTML.

	Jakub
Richard Biener Feb. 28, 2024, 8:33 a.m. UTC | #5
On Wed, Feb 28, 2024 at 9:25 AM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Wed, Feb 28, 2024 at 08:58:08AM +0100, Richard Biener wrote:
> > Incidentially this looks like something fit for a google summer of code project.
> > Ideally it would hook into print-tree.cc providing an alternate
> > structured output.
> > It currently prints in the style
> >
> >  <function_decl 0x7ffff71bc600 bswap16
> >     type <function_type 0x7ffff71ba5e8
> >         type <integer_type 0x7ffff702b540 short unsigned int public unsigned HI
> >             size <integer_cst 0x7ffff702d108 constant 16>
> >             unit-size <integer_cst 0x7ffff702d120 constant 2>
> >             align:16 warn_if_not_align:0 symtab:0 alias-set -1
> > canonical-type 0x7ffff702b540 precision:16 min <integer_cst
> > 0x7ffff702d138 0> max <integer_cst 0x7ffff702d0f0 65535>>
> >         QI
> >         size <integer_cst 0x7ffff702d048 constant 8>
> >         unit-size <integer_cst 0x7ffff702d060 constant 1>
> > ...
> >
> > where you can see it follows tree -> tree edges up to some depth
> > (and avoids repeated expansion).  When debugging that's all I have
> > and I have to follow edges by matching up the raw addresses printed,
> > re-dumping those that didn't get expanded.  HTML would be indeed
> > so much nicer here (and a more complete output).
>
> I think keeping the current format of what is printed but optionally just
> turn all those addresses in there into hyperlinks which would expand the
> other trees would be nice.  Maybe also allow just hovering on the link and
> show the other tree printed might be nice too.
> Folding it all into just <function_decl ... bswap <type link> ...>
> would mean one can't quickly access just the min/max or fn return type
> etc.  We might need some parameter how deep to go (and/or how many trees to
> dump at most) so that we don't dump into HTML gigabytes of data when asking
> to print say a large BIND_EXPR into HTML.

Btw, when in a debugging session I'd be fine with something like

(gdb) html-tree $3

creating a temporary file and spawning a window using the default .html
file handler with this being implemented in the python bindings similar
to how we handle dot-fn.  I realize that some terminals might support
parts of that "inline" (I bet my xterm doesn't ;)).

I think HTML might be machine-readable enough for this purpose
so indirecting via JSON or XML or whatnot isn't necessary (we don't
aim for this to be a way to extract GENERIC and feed it to another compiler).

Richard.

>
>         Jakub
>
David Malcolm Feb. 28, 2024, 3:14 p.m. UTC | #6
On Wed, 2024-02-28 at 08:58 +0100, Richard Biener wrote:
> On Tue, Feb 27, 2024 at 10:20 PM Robert Dubner <rdubner@symas.com>
> wrote:
> > 
> > Richard,
> > 
> > Thank you very much for your comments.
> > 
> > When I set out to create the capability, I had a "specification" in
> > mind.
> > 
> > I didn't have a clue how to create a GENERIC tree that could be fed
> > to the
> > middle end in a way that would successfully result in an
> > executable.  And I
> > needed to be able to do that in order to proceed with the project
> > of
> > creating a COBOL front end.
> > 
> > So, I came up with the idea of using GCC to compile simple
> > programs, and to
> > hook into the compiler to examine the trees fed to the middle end,
> > and to
> > display those trees in the human-readable format I needed to
> > understand
> > them.  And that's what I did.
> > 
> > My first incarnation generated pure text files, and I used that to
> > get
> > going.
> > 
> > After a while I realized that when I used the output file, I was
> > spending a
> > lot of time searching through the text files.  And I had the
> > brainstorm!
> > Hyperlinks!  HTML files!  We have the technology!  So, I created
> > the .HTML
> > files as well.
> > 
> > I found this useful to the point of necessity in order to learn how
> > to
> > generate the GENERIC trees.  I believe it would be equally useful
> > to the
> > next developer who, for whatever reason, needs to understand, on a
> > "You need
> > to learn the alphabet before you can learn how to read" level, what
> > the
> > middle end requires from a GENERIC tree generated by a front end.
> > 
> > But I've never used it on a complex program. I've used it only to
> > learn how
> > to create the GENERIC nodes for very particular things, and so I
> > would use
> > the -fdump-generic-nodes feature on a very simple C program that
> > demonstrated, in isolation, the feature I needed.  Once I figured
> > it out, I
> > would create front end C routines or macros that used the
> > tree.h/tree.cc
> > features to build those GENERIC trees, and then I would move on.
> > 
> > I decided to offer it up here, in order to to learn how to create
> > patches
> > and to get
> > to know the people and the process, as well as from the desire to
> > share it.
> > And instantly I got the "How about a machine-readable format?"
> > comments.
> > Which are reasonable.  So, because it wasn't hard, I hacked at the
> > existing
> > code to create a JSON output.  (But I remind you that up until now,
> > nobody
> > seems to have needed a JSON representation.)
> > 
> > And your observation that the human readable representation could
> > be made
> > from the JSON representation is totally accurate.
> > 
> > But that wasn't my specification.  My specification was "A tool so
> > that a
> > human being can examine a simple GENERIC tree to learn how it's
> > done."
> > 
> > But it seems to me that we are now moving into the realm of a new
> > specification.
> > 
> > Said another way:  To go from "A human readable representation of a
> > simple
> > GENERIC tree" to "A machine readable JSON representation of an
> > arbitrarily
> > complex GENERIC tree, from which a human readable representation
> > can be
> > created" means, in effect, starting over on a different project
> > that I don't
> > need.  I already *have* a project that I am working on -- the COBOL
> > front
> > end.
> > 
> > The complexity of GENERIC trees is, in my experienced opinion, an
> > obstacle
> > for the creation of front ends.  The GCC Internals document has a
> > lot of
> > information, but to go from it to a front end is like using the
> > maintenance
> > manual for an F16 fighter to try to learn to fly the aircraft.
> > 
> > The program "main(){}" generates a tree with over seventy nodes.  I
> > see no
> > way to document why that's true; it's all arbitrary in the sense
> > that "this
> > is how GCC works".  -fdump-generic-nodes made it possible for me to
> > figure
> > out how those nodes are connected and, thus, how to create a new
> > front end.
> > I figure that other developers might find it useful, as well.
> > 
> > I guess I am saying that I am not, at this time, able to work on a
> > whole
> > different tool.  I think what I have done so far does something
> > useful that
> > doesn't seem to otherwise exist in GCC.
> > 
> > I suppose the question for you is, "Is it useful enough?"
> > 
> > I won't be offended if the answer is "No" and I hope you won't be
> > offended
> > by my not having the bandwidth to address your very thoughtful and
> > valid
> > observations about how it could be better.
> 
> No offense taken - I did realize how useful this was to you (and
> specifically
> the hyper-linking looked even very useful to me!).  I often lament
> the lack
> of domain-specific visualization tools for the various data
> structures GCC
> has - having something for GENERIC would be very welcome.
> 
> We have for example ways to dump graphviz .dot format graphs of the
> CFG
> and some other data structures and do that natively, not via JSON
> indirection.

FWIW for GCC 15 I've been experimenting with adding a
text_art::tree_widget class; with that, the analyzer can visualize an
ana::program_state instance like this (potentially with colorization in
suitable terminals):

State
├─ Region Model
│  ├─ Current Frame: frame: ‘test_7’@1
│  ├─ Store
│  │  ╰─ root region
│  │     ╰─ (*INIT_VAL(a_10(D)))
│  │        ╰─ bytes 12-15: ‘int’ {(int)42}
│  ╰─ Constraints
│     ├─ Equivalence class ec0
│     │  ├─ (void *)0B
│     │  ╰─ ‘0B’
│     ├─ Equivalence class ec1
│     │  ╰─ INIT_VAL(a_10(D))
│     ├─ Equivalence class ec2
│     │  ╰─ (INIT_VAL(a_10(D))+(sizetype)12)
│     ├─ ec1: {INIT_VAL(a_10(D))} != ec0: {(void *)0B == [m_constant]‘0B’}
│     ╰─ ec2: {(INIT_VAL(a_10(D))+(sizetype)12)} != ec0: {(void *)0B == [m_constant]‘0B’}
╰─ ‘malloc’ state machine
   ╰─ 0x62082e0: (INIT_VAL(a_10(D))+(sizetype)12): assumed-non-null (in frame: ‘test_7’@1)

and such visualizations could be added for other hierarchical data
structures.  Also, because it uses text_art::widget, the content at a
tree node doesn't have to be purely textual, and we could do things
like the following (which is a mockup):

State
├─ Region Model           Bound value │ Effective value
│  ├─ Stack                           │
│  │  ├─ frame@0 'foo'                │
│  │  │  ├─ 'i'           (int)42     │ 
│  │  │  ╰─ 'j'                       │ UNINIT(int)
│  │  ╰─ frame@1 'bar'                │
│  │     ╰─ 'k'                       │
│  │        ╰─ [3]                    │
│  │           ├─ .x      INIT_VAL(p) │
│  │           ╰─ .y      INIT_VAL(q) │
│  ╰─ Globals                         │
│     ╰─ 'baz'                        │
├─ Constraints
│  ╰─ (etc)
╰─ 'malloc' state
   ╰─ CONJURED_VALUE('ptr') unchecked('free')


That said, our "tree" structure is arguably a directed graph rather
than a tree (consider e.g. types)

> 
> Incidentially this looks like something fit for a google summer of
> code project.
> Ideally it would hook into print-tree.cc providing an alternate
> structured output.
> It currently prints in the style
> 
>  <function_decl 0x7ffff71bc600 bswap16
>     type <function_type 0x7ffff71ba5e8
>         type <integer_type 0x7ffff702b540 short unsigned int public
> unsigned HI
>             size <integer_cst 0x7ffff702d108 constant 16>
>             unit-size <integer_cst 0x7ffff702d120 constant 2>
>             align:16 warn_if_not_align:0 symtab:0 alias-set -1
> canonical-type 0x7ffff702b540 precision:16 min <integer_cst
> 0x7ffff702d138 0> max <integer_cst 0x7ffff702d0f0 65535>>
>         QI
>         size <integer_cst 0x7ffff702d048 constant 8>
>         unit-size <integer_cst 0x7ffff702d060 constant 1>
> ...
> 
> where you can see it follows tree -> tree edges up to some depth
> (and avoids repeated expansion).  When debugging that's all I have
> and I have to follow edges by matching up the raw addresses printed,
> re-dumping those that didn't get expanded.  HTML would be indeed
> so much nicer here (and a more complete output).
> 
> From a maintainance point I think it's important to have "dump a tree
> node"
> once, so when fields are added or deemed useful for presenting in a
> dump
> you don't have to chase down more than one place.  Maintenance is
> also
> the reason to not simply accept your contribution as-is.

Presumably we'd want some kind of "visitor" code that captures visiting
the fields of each type in one place, allowing for different consumers
of this data (HTML generation, JSON generation etc).  Though I'm not
sure how best to express this double-dispatch (by templates, vfunc, or
whatnot).

We already have some of this in the form of gengtype and what it
generates, but I suspect we don't want to add further reliance on
gengtype.

> 
> I do hope this eventually gets picked up.  I've added a project idea
> to https://gcc.gnu.org/wiki/SummerOfCode
> and would be willing to mentor it.

FWIW you added it to the "Other projects and project ideas" below
"Other Project Ideas", where it's much less likely to get noticed than
under the "Selected Project Ideas".
> 
> Oh, and I'm looking forward to the actual Cobol work!

Likewise

[...snip...]

Dave
Richard Biener Feb. 29, 2024, 7:33 a.m. UTC | #7
On Wed, Feb 28, 2024 at 4:14 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Wed, 2024-02-28 at 08:58 +0100, Richard Biener wrote:
> > On Tue, Feb 27, 2024 at 10:20 PM Robert Dubner <rdubner@symas.com>
> > wrote:
> > >
> > > Richard,
> > >
> > > Thank you very much for your comments.
> > >
> > > When I set out to create the capability, I had a "specification" in
> > > mind.
> > >
> > > I didn't have a clue how to create a GENERIC tree that could be fed
> > > to the
> > > middle end in a way that would successfully result in an
> > > executable.  And I
> > > needed to be able to do that in order to proceed with the project
> > > of
> > > creating a COBOL front end.
> > >
> > > So, I came up with the idea of using GCC to compile simple
> > > programs, and to
> > > hook into the compiler to examine the trees fed to the middle end,
> > > and to
> > > display those trees in the human-readable format I needed to
> > > understand
> > > them.  And that's what I did.
> > >
> > > My first incarnation generated pure text files, and I used that to
> > > get
> > > going.
> > >
> > > After a while I realized that when I used the output file, I was
> > > spending a
> > > lot of time searching through the text files.  And I had the
> > > brainstorm!
> > > Hyperlinks!  HTML files!  We have the technology!  So, I created
> > > the .HTML
> > > files as well.
> > >
> > > I found this useful to the point of necessity in order to learn how
> > > to
> > > generate the GENERIC trees.  I believe it would be equally useful
> > > to the
> > > next developer who, for whatever reason, needs to understand, on a
> > > "You need
> > > to learn the alphabet before you can learn how to read" level, what
> > > the
> > > middle end requires from a GENERIC tree generated by a front end.
> > >
> > > But I've never used it on a complex program. I've used it only to
> > > learn how
> > > to create the GENERIC nodes for very particular things, and so I
> > > would use
> > > the -fdump-generic-nodes feature on a very simple C program that
> > > demonstrated, in isolation, the feature I needed.  Once I figured
> > > it out, I
> > > would create front end C routines or macros that used the
> > > tree.h/tree.cc
> > > features to build those GENERIC trees, and then I would move on.
> > >
> > > I decided to offer it up here, in order to to learn how to create
> > > patches
> > > and to get
> > > to know the people and the process, as well as from the desire to
> > > share it.
> > > And instantly I got the "How about a machine-readable format?"
> > > comments.
> > > Which are reasonable.  So, because it wasn't hard, I hacked at the
> > > existing
> > > code to create a JSON output.  (But I remind you that up until now,
> > > nobody
> > > seems to have needed a JSON representation.)
> > >
> > > And your observation that the human readable representation could
> > > be made
> > > from the JSON representation is totally accurate.
> > >
> > > But that wasn't my specification.  My specification was "A tool so
> > > that a
> > > human being can examine a simple GENERIC tree to learn how it's
> > > done."
> > >
> > > But it seems to me that we are now moving into the realm of a new
> > > specification.
> > >
> > > Said another way:  To go from "A human readable representation of a
> > > simple
> > > GENERIC tree" to "A machine readable JSON representation of an
> > > arbitrarily
> > > complex GENERIC tree, from which a human readable representation
> > > can be
> > > created" means, in effect, starting over on a different project
> > > that I don't
> > > need.  I already *have* a project that I am working on -- the COBOL
> > > front
> > > end.
> > >
> > > The complexity of GENERIC trees is, in my experienced opinion, an
> > > obstacle
> > > for the creation of front ends.  The GCC Internals document has a
> > > lot of
> > > information, but to go from it to a front end is like using the
> > > maintenance
> > > manual for an F16 fighter to try to learn to fly the aircraft.
> > >
> > > The program "main(){}" generates a tree with over seventy nodes.  I
> > > see no
> > > way to document why that's true; it's all arbitrary in the sense
> > > that "this
> > > is how GCC works".  -fdump-generic-nodes made it possible for me to
> > > figure
> > > out how those nodes are connected and, thus, how to create a new
> > > front end.
> > > I figure that other developers might find it useful, as well.
> > >
> > > I guess I am saying that I am not, at this time, able to work on a
> > > whole
> > > different tool.  I think what I have done so far does something
> > > useful that
> > > doesn't seem to otherwise exist in GCC.
> > >
> > > I suppose the question for you is, "Is it useful enough?"
> > >
> > > I won't be offended if the answer is "No" and I hope you won't be
> > > offended
> > > by my not having the bandwidth to address your very thoughtful and
> > > valid
> > > observations about how it could be better.
> >
> > No offense taken - I did realize how useful this was to you (and
> > specifically
> > the hyper-linking looked even very useful to me!).  I often lament
> > the lack
> > of domain-specific visualization tools for the various data
> > structures GCC
> > has - having something for GENERIC would be very welcome.
> >
> > We have for example ways to dump graphviz .dot format graphs of the
> > CFG
> > and some other data structures and do that natively, not via JSON
> > indirection.
>
> FWIW for GCC 15 I've been experimenting with adding a
> text_art::tree_widget class; with that, the analyzer can visualize an
> ana::program_state instance like this (potentially with colorization in
> suitable terminals):
>
> State
> ├─ Region Model
> │  ├─ Current Frame: frame: ‘test_7’@1
> │  ├─ Store
> │  │  ╰─ root region
> │  │     ╰─ (*INIT_VAL(a_10(D)))
> │  │        ╰─ bytes 12-15: ‘int’ {(int)42}
> │  ╰─ Constraints
> │     ├─ Equivalence class ec0
> │     │  ├─ (void *)0B
> │     │  ╰─ ‘0B’
> │     ├─ Equivalence class ec1
> │     │  ╰─ INIT_VAL(a_10(D))
> │     ├─ Equivalence class ec2
> │     │  ╰─ (INIT_VAL(a_10(D))+(sizetype)12)
> │     ├─ ec1: {INIT_VAL(a_10(D))} != ec0: {(void *)0B == [m_constant]‘0B’}
> │     ╰─ ec2: {(INIT_VAL(a_10(D))+(sizetype)12)} != ec0: {(void *)0B == [m_constant]‘0B’}
> ╰─ ‘malloc’ state machine
>    ╰─ 0x62082e0: (INIT_VAL(a_10(D))+(sizetype)12): assumed-non-null (in frame: ‘test_7’@1)
>
> and such visualizations could be added for other hierarchical data
> structures.  Also, because it uses text_art::widget, the content at a
> tree node doesn't have to be purely textual, and we could do things
> like the following (which is a mockup):
>
> State
> ├─ Region Model           Bound value │ Effective value
> │  ├─ Stack                           │
> │  │  ├─ frame@0 'foo'                │
> │  │  │  ├─ 'i'           (int)42     │
> │  │  │  ╰─ 'j'                       │ UNINIT(int)
> │  │  ╰─ frame@1 'bar'                │
> │  │     ╰─ 'k'                       │
> │  │        ╰─ [3]                    │
> │  │           ├─ .x      INIT_VAL(p) │
> │  │           ╰─ .y      INIT_VAL(q) │
> │  ╰─ Globals                         │
> │     ╰─ 'baz'                        │
> ├─ Constraints
> │  ╰─ (etc)
> ╰─ 'malloc' state
>    ╰─ CONJURED_VALUE('ptr') unchecked('free')
>
>
> That said, our "tree" structure is arguably a directed graph rather
> than a tree (consider e.g. types)
>
> >
> > Incidentially this looks like something fit for a google summer of
> > code project.
> > Ideally it would hook into print-tree.cc providing an alternate
> > structured output.
> > It currently prints in the style
> >
> >  <function_decl 0x7ffff71bc600 bswap16
> >     type <function_type 0x7ffff71ba5e8
> >         type <integer_type 0x7ffff702b540 short unsigned int public
> > unsigned HI
> >             size <integer_cst 0x7ffff702d108 constant 16>
> >             unit-size <integer_cst 0x7ffff702d120 constant 2>
> >             align:16 warn_if_not_align:0 symtab:0 alias-set -1
> > canonical-type 0x7ffff702b540 precision:16 min <integer_cst
> > 0x7ffff702d138 0> max <integer_cst 0x7ffff702d0f0 65535>>
> >         QI
> >         size <integer_cst 0x7ffff702d048 constant 8>
> >         unit-size <integer_cst 0x7ffff702d060 constant 1>
> > ...
> >
> > where you can see it follows tree -> tree edges up to some depth
> > (and avoids repeated expansion).  When debugging that's all I have
> > and I have to follow edges by matching up the raw addresses printed,
> > re-dumping those that didn't get expanded.  HTML would be indeed
> > so much nicer here (and a more complete output).
> >
> > From a maintainance point I think it's important to have "dump a tree
> > node"
> > once, so when fields are added or deemed useful for presenting in a
> > dump
> > you don't have to chase down more than one place.  Maintenance is
> > also
> > the reason to not simply accept your contribution as-is.
>
> Presumably we'd want some kind of "visitor" code that captures visiting
> the fields of each type in one place, allowing for different consumers
> of this data (HTML generation, JSON generation etc).  Though I'm not
> sure how best to express this double-dispatch (by templates, vfunc, or
> whatnot).
>
> We already have some of this in the form of gengtype and what it
> generates, but I suspect we don't want to add further reliance on
> gengtype.
>
> >
> > I do hope this eventually gets picked up.  I've added a project idea
> > to https://gcc.gnu.org/wiki/SummerOfCode
> > and would be willing to mentor it.
>
> FWIW you added it to the "Other projects and project ideas" below
> "Other Project Ideas", where it's much less likely to get noticed than
> under the "Selected Project Ideas".

Yeah, I didn't feel it was "Selected", but maybe that doesn't mean
anything?

> >
> > Oh, and I'm looking forward to the actual Cobol work!
>
> Likewise
>
> [...snip...]
>
> Dave
>
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a74761b7ab3..81922b0884c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1441,6 +1441,7 @@  OBJS = \
 	domwalk.o \
 	double-int.o \
 	dse.o \
+	dump-generic-nodes.o \
 	dumpfile.o \
 	dwarf2asm.o \
 	dwarf2cfi.o \
@@ -3857,7 +3858,7 @@  PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H)
coretypes.h $(TM_H) \
   hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h
\
   lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \
   pass-instances.def params.list $(srcdir)/../include/gomp-constants.h \
-  $(EXPR_H) $(srcdir)/analyzer/*.h
+  $(EXPR_H) $(srcdir)/analyzer/*.h dump-generic-nodes.h
 
 # generate the 'build fragment' b-header-vars
 s-header-vars: Makefile
diff --git a/gcc/common.opt b/gcc/common.opt
index 51c4a17da83..751b9b1f0cc 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1583,6 +1583,10 @@  fdump-passes
 Common Var(flag_dump_passes) Init(0)
 Dump optimization passes.
 
+fdump-generic-nodes
+Common Var(flag_dump_generic_nodes) Init(0)
+Dump GENERIC trees for each function in three files: <funcname>.nodes,
<funcname>.nodes.html, and <funcname>.json
+
 fdump-unnumbered
 Common Var(flag_dump_unnumbered)
 Suppress output of instruction numbers, line number notes and addresses
in debugging dumps.
diff --git a/gcc/dump-generic-nodes.cc b/gcc/dump-generic-nodes.cc
new file mode 100644
index 00000000000..d44119116d2
--- /dev/null
+++ b/gcc/dump-generic-nodes.cc
@@ -0,0 +1,1958 @@ 
+/* Prints out a tree of generic/gimple nodes in human readable form, both
in
+   straight text and in HTML. The entry point is dump_generic_nodes().
+
+   Copyright(C) 1990-2024 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or(at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "varasm.h"
+#include "print-rtl.h"
+#include "stor-layout.h"
+#include "langhooks.h"
+#include "tree-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-cfg.h"
+#include "dumpfile.h"
+
+#undef DEFTREESTRUCT
+#define DEFTREESTRUCT(VAL, NAME) NAME,
+static const char *ts_enum_names[] =
+  {
+  #include "treestruct.def"
+  };
+#undef DEFTREESTRUCT
+
+#define ADD_FLAG(accessor,text)if(accessor(node)){strcat(ach," " text);}
+
+static FILE *ftext = NULL;
+static FILE *fhtml = NULL;
+static FILE *fjson = NULL;
+
+static int json_level = 0;
+static const char *json_comma;
+static const int spaces_per_indent = 2;
+
+static void rjd_print_node(tree node);
+
+static int phase = 1;
+
+/* Define the hash table of nodes already seen.
+   Such nodes are not repeated; brief cross-references are used.  */
+
+struct TREE
+  {
+  tree this_node;
+  int     seen;
+  } ;
+
+static struct TREE *nodes = NULL;
+static int GV_size_of_tree = 0;
+static int GV_number_of_nodes = 0;
+
+static void
+json_fprintf(const char *format, ...) __attribute__ ((format (printf, 1,
2)));
+
+static void
+json_fprintf(const char *format, ...)
+{
+  char ach1[2048];
+  if( phase == 2 )
+    {
+    va_list args;
+    va_start(args, format);
+    vsnprintf(ach1, sizeof(ach1), format, args);
+    va_end(args);
+    fprintf(fjson, "%s", ach1);
+    }
+}
+
+static void
+json_indent()
+{
+  for(int i=0; i<json_level*spaces_per_indent; i++)
+    {
+    json_fprintf(" ");
+    }
+}
+
+static void
+json_newline()
+{
+  json_fprintf("%s\n", json_comma);
+  json_indent();
+}
+
+static int
+find_node_in_nodes(tree node)
+{
+  // This is an O(n^2) abomination.  At a suitable time, replace it with
a
+  // hashed map!
+  int retval = -1;
+  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
+  for(int i=0; i<GV_number_of_nodes; i++)
+    {
+    if( nodes[i].this_node == node )
+      {
+      retval = i;
+      break;
+      }
+    }
+  return retval;
+}
+
+static int
+add_node_to_nodes(tree node)
+{
+  // Assume we know the node isn't there yet:
+  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
+  if( GV_number_of_nodes == GV_size_of_tree )
+    {
+    // We've run out of room.  Double the number of nodes:
+    GV_size_of_tree *= 2;
+    if( GV_size_of_tree == 0 )
+      {
+      GV_size_of_tree = 64;
+      }
+    struct TREE *newnodes =
+               (struct TREE *)xmalloc( GV_size_of_tree * sizeof(struct
TREE) );
+    memcpy(newnodes,nodes, GV_number_of_nodes * sizeof(struct TREE));
+    free(nodes);
+    nodes = newnodes;
+    }
+  gcc_assert(GV_number_of_nodes <= GV_size_of_tree);
+  nodes[GV_number_of_nodes].this_node = node;
+  nodes[GV_number_of_nodes].seen = 0;
+  GV_number_of_nodes += 1;
+  return GV_number_of_nodes-1;
+}
+
+#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
+
+static void
+rjd_fprintf(const char *format, ...)
+{
+  // NOTE: I painfully learned that mixing fprintf() and vfprintf() calls
+  // can result in hard-to-find memory allocation bugs.  Probably worth
+  // tracking down, but the the problem shows up in the libc6 library
+  // routines.
+  char ach1[2048];
+  char ach2[4196];
+  char ach3[4196];
+  if( phase == 2 )
+    {
+    va_list args;
+    va_start(args, format);
+    vsnprintf(ach1, sizeof(ach1), format, args);
+    va_end(args);
+
+    fprintf(ftext, "%s", ach1);
+
+    // Copy ach1 to ach2, replacing '\n' with "<br>"
+    char *p1 = ach1;
+    char *p2 = ach2;
+
+    while( *p1 )
+        {
+        if(*p1 == '\n')
+          {
+          p1 += 1;
+          *p2 = '\0';
+          strcat(p2, "<br>");
+          p2 = ach2 + strlen(ach2);
+          }
+        else
+          {
+          *p2++ = *p1++;
+          }
+        }
+    size_t ach1_length = strlen(ach1);
+
+    // To make the HTML more-or-less readable, if ach1 ends with a
newline,
+    // make ach2 end with a newline as well.
+    if( ach1_length && ach1[ach1_length-1] == '\n' )
+      {
+      *p2++ = '\n';
+      }
+    *p2++ = '\0';
+
+    // We now look for NodeNumberNNN and replace it with
+    // <a href="#NodeNumberNNN">NodeNumberNNN</a>
+
+    char *pleft = strstr(ach2, "NodeNumber");
+    if( pleft )
+      {
+      char *pright = pleft + strlen("NodeNumber");
+      while( *pright >= '0' && *pright <= '9' )
+        {
+        pright += 1;
+        }
+      memset(ach3, 0, sizeof(ach3));
+      memcpy(ach3, ach2, pleft - ach2);
+      strcat(ach3, "<a href=\"#" );
+      char *p = ach3 + strlen(ach3);
+      memcpy(p, pleft, pright-pleft);
+      strcat(ach3,"\">");
+      p = ach3 + strlen(ach3);
+      memcpy(p, pleft, pright-pleft);
+      p = ach3 + strlen(ach3);
+      strcat(ach3,"</a>");
+      p = ach3 + strlen(ach3);
+      strcpy(p, pright);
+      strcpy(ach2, ach3);
+      }
+
+    fprintf(fhtml, "%s", ach2);
+    }
+}
+
+#define NOT_QUOTED false
+static void
+json_namevalue(const char *name, const char *value, bool quoted = true)
+{
+  if( phase == 2 )
+    {
+    json_newline();
+    if( quoted )
+      {
+      json_fprintf("\"%s\":\"%s\"", name, value);
+      }
+    else
+      {
+      json_fprintf("\"%s\":%s", name, value);
+      }
+    }
+}
+
+static void
+html_boilerplate(char *title=NULL)
+{
+  if( fhtml )
+    {
+    if( title )
+      {
+      fprintf(fhtml,
+              "<!DOCTYPE html>\n"
+              "<html lang=\"en\">\n"
+              "  <head>\n"
+              "    <meta charset=\"utf-8\">\n"
+              "    <title>%s</title>\n"
+              "    <style>\n"
+              "    body\n"
+              "        {\n"
+              "        font-family: courier, serif;\n"
+              "        font-size: 13px;\n"
+              "        }\n"
+              "    </style>\n"
+              "  </head>\n"
+              "  <body>\n", title);
+      }
+    else
+      {
+      fprintf(fhtml, "%s",
+              "  </body>\n"
+              "</html>\n");
+      }
+    }
+}
+
+/* Print a node in brief fashion*/
+
+static void
+print_name(const char *prefix, tree node)
+{
+  if( node && TREE_CODE_CLASS(TREE_CODE(node)) == tcc_declaration )
+    {
+    tree name = DECL_NAME(node);
+    if( name )
+      {
+      rjd_fprintf("%s\"%s\"", prefix, IDENTIFIER_POINTER(name));
+      }
+    }
+}
+
+static void
+rjd_subtree(const char *subtree_name, tree subtree)
+{
+  if( subtree )
+    {
+    if( phase == 1 )
+      {
+      // Make sure the subtree is in the list of nodes.  And, yes, this
+      // is a potential headachy recursivy thingummy.  But computers are
+      // good at that.
+      rjd_print_node(subtree);
+
+      int subtree_node_number = find_node_in_nodes(subtree);
+      if( subtree_node_number == -1 )
+        {
+        printf("Run in circles, scream and shout!\n");
+        exit(1);
+        }
+
+      }
+    else if( phase == 2 )
+      {
+      char ach[512];
+      int node_number = find_node_in_nodes(subtree);
+      if( node_number >= 0 )
+        {
+        rjd_fprintf("%s: NodeNumber%d", subtree_name, node_number);
+
+        // Handle a few subthings that are very common, and useful to
see:
+        gcc_assert(node_number < GV_number_of_nodes
+                   && GV_number_of_nodes <= GV_size_of_tree);
+        tree subnode = nodes[node_number].this_node;
+        if( subnode )
+          {
+          enum tree_code subcode = TREE_CODE(subnode);
+
+          // After the NodeNumber, print the code name.  Unless it is an
+          // integer_cst, because we convert that to int32 or uint64
later.
+          if( subcode != INTEGER_CST )
+            {
+            rjd_fprintf(" %s", get_tree_code_name(subcode));
+            }
+
+          if( subcode == IDENTIFIER_NODE && IDENTIFIER_POINTER(subnode) )
+            {
+            rjd_fprintf(" \"%s\"", IDENTIFIER_POINTER(subnode));
+            }
+
+          print_name(" ", subnode);
+
+          if( subcode == INTEGER_CST )
+            {
+            tree int_cst_type = TREE_TYPE(subnode);
+            tree_code int_cst_type_code = TREE_CODE(int_cst_type);
+
+            if( int_cst_type_code == POINTER_TYPE )
+              {
+              rjd_fprintf(" pointer");
+              }
+            else if( int_cst_type_code == INTEGER_TYPE)
+              {
+              // The int_cst_type is an integer_type, so...
+              tree min_value = TYPE_MIN_VALUE_RAW(int_cst_type);
+              print_dec(wi::to_wide(min_value),
+                        ach,
+                        TYPE_SIGN(TREE_TYPE(min_value)));
+              if( strcmp( ach, "0") )
+                {
+                rjd_fprintf(" int",ach);
+                }
+              else
+                {
+                rjd_fprintf(" uint",ach);
+                }
+
+              tree size_in_bits = TYPE_SIZE(int_cst_type);
+
+              print_dec(wi::to_wide(size_in_bits),
+                        ach,
+                        TYPE_SIGN(TREE_TYPE(size_in_bits)));
+              rjd_fprintf(ach);
+
+              print_dec(wi::to_wide(subnode),
+                        ach, TYPE_SIGN(TREE_TYPE(subnode)));
+              }
+
+            print_dec(wi::to_wide(subnode),
+                      ach,
+                      TYPE_SIGN(TREE_TYPE(subnode)));
+            rjd_fprintf(" %s",ach);
+            }
+
+          if( subcode == DECL_EXPR )
+            {
+            int len = TREE_OPERAND_LENGTH(subnode);
+            if( len )
+              {
+              print_name(" ", TREE_OPERAND(subnode, 0));
+              }
+            }
+
+          if( subcode == CALL_EXPR )
+            {
+            int len = TREE_OPERAND_LENGTH(subnode);
+            if( len > 1)
+              {
+              tree addr_expression = TREE_OPERAND(subnode, 1);
+              int len2 = TREE_OPERAND_LENGTH(addr_expression);
+              if( len2 )
+                {
+                print_name(" ", TREE_OPERAND(addr_expression, 0));
+                }
+              }
+            }
+
+          if( subcode == ADDR_EXPR  )
+            {
+            int len = TREE_OPERAND_LENGTH(subnode);
+            if( len > 0)
+              {
+              print_name(" ", TREE_OPERAND(subnode, 0));
+              }
+            }
+
+          if( subcode == MODIFY_EXPR )
+            {
+            int len = TREE_OPERAND_LENGTH(subnode);
+            if( len > 0)
+              {
+              tree target = TREE_OPERAND(subnode, 0);
+              tree_code target_code = TREE_CODE(target);
+              tree_code_class target_code_class =
TREE_CODE_CLASS(target_code);
+              if( target_code_class == tcc_declaration )
+                {
+                print_name(" ", target);
+                }
+              else if( target_code == COMPONENT_REF
+                                      && target_code_class ==
tcc_reference )
+                {
+                int len = TREE_OPERAND_LENGTH(target);
+                tree structure = NULL_TREE;
+                tree field     = NULL_TREE;
+                if( len > 0 )
+                  {
+                  structure = TREE_OPERAND(target, 0);
+                  }
+                if( len > 1 )
+                  {
+                  field = TREE_OPERAND(target, 1);
+                  }
+                print_name(" ", structure);
+                print_name("::", field);
+                }
+              }
+            }
+
+          rjd_fprintf("\n");
+          }
+        // In contrast, for the JSON output, we just want the node
number:
+        // But we need to intervene in the case where the subtree name
starts
+        // off with "operand[nnn]".  We are putting them in JSON arraysm,
so we
+        // need to eliminate that name
+        if( strstr(subtree_name, "constructor_elt[") != subtree_name )
+          {
+          // constructors are handled separately because they have both
index
+          // and value members.  Search for "constructor_elt" to find
that code.
+          if(    strstr(subtree_name, "operand[") == subtree_name
+              || strstr(subtree_name, "tree_vector_element[") ==
subtree_name
+              || strstr(subtree_name, "statement_list[") == subtree_name
+              || strstr(subtree_name, "nonlocalized_var[") ==
subtree_name
+              )
+            {
+            // We are elminating the subtree name
+            json_newline();
+            }
+          else
+            {
+            json_namevalue(subtree_name, "", NOT_QUOTED);
+            }
+          json_fprintf("{\"node\":%d}", node_number);
+          }
+        }
+      else
+        {
+        // This can happen as a result of certain compilation
+        // errors.   Just ignore them.
+        }
+      }
+    }
+  else
+    {
+    if( strstr(subtree_name, "operand[") == subtree_name )
+      {
+      json_newline();
+      json_fprintf("{\"node\":null}");
+      }
+
+    }
+
+}
+
+static void
+json_flags(const char *name, const char *ach)
+{
+  const char *p = ach+1;  // Skip past the initial space
+  json_namevalue(name, "",  NOT_QUOTED);
+  json_level += 1;
+  json_comma = "";
+  json_fprintf("\n");
+  json_indent();
+  json_fprintf("{");
+
+  while( *p )
+    {
+    const char *pend = strchr(p, ' ');
+    if( !pend )
+      {
+      pend = p + strlen(p);
+      }
+    char achName[256];
+    char *d = achName;
+    while(p < pend )
+      {
+      *d++ = *p++;
+      }
+    *d++= '\0';
+    if( *p == ' ' )
+      {
+      p += 1;
+      }
+    json_namevalue(achName, "true", NOT_QUOTED);
+    json_comma = ",";
+    }
+  json_fprintf("\n");
+  json_indent();
+  json_fprintf("}");
+
+  json_level -= 1;
+  json_comma = ",";
+}
+
+static void
+json_start_array(const char *name)
+{
+  // We will set up a JSON array of operands
+  json_namevalue(name,"", NOT_QUOTED);
+  json_fprintf("\n");
+  json_level += 1;
+  json_indent();
+  json_fprintf("[");
+  json_comma = "";
+}
+static void
+json_finish_array()
+{
+  json_fprintf("\n");
+  json_indent();
+  json_fprintf("]");
+  json_level -= 1;
+  json_comma = ",";
+}
+
+static void
+rjd_print_node(tree node)
+{
+  char ach[4096];
+  enum tree_code_class tclass;
+  int len;
+  int i;
+  expanded_location xloc;
+  enum tree_code code;
+
+  if(node == 0)
+    {
+    // When handled a NULL, just return
+    return;
+    }
+
+  int node_number = find_node_in_nodes(node);
+  if( phase == 1 )
+    {
+    if( node_number != -1 )
+      {
+      // We're building the list of nodes, and we've already processed
this
+      // node:
+      return;
+      }
+
+    // We are building the list of nodes, and this is a new one:
+    node_number = add_node_to_nodes(node);
+    }
+  // From here on out, we know that node_number is valid, whether in
+  // phase 1 or phase 2
+
+  code = TREE_CODE(node);
+
+  /* It is unsafe to look at any other fields of a node with ERROR_MARK
or
+     invalid code.  */
+  if(code == ERROR_MARK || code >= MAX_TREE_CODES)
+    {
+    rjd_fprintf("This node is unsafe.  The reported TREE_CODE is
%d\n",code);
+    return;
+    }
+
+  tclass = TREE_CODE_CLASS(code);
+
+  /* Announce the coming of a new node: */
+  if( phase==2 && fhtml )
+    {
+    fprintf(fhtml, "<p id=\"NodeNumber%d\">\n", node_number);
+    }
+  rjd_fprintf("***********************************This is
NodeNumber%d\n",
+              node_number);
+
+  /* Print the NodeNumber in a more canonical form: */
+  rjd_fprintf("(%p) NodeNumber%d\n", node, node_number);
+
+  // Print the tree_code for this node
+  rjd_fprintf("tree_code: %s\n", get_tree_code_name(code));
+  json_namevalue("tree_code", get_tree_code_name(code));
+
+  // It might be useful to see the class
+  const char *classtxt;
+  switch(tclass)
+    {
+    case tcc_exceptional:
+      classtxt = "tcc_exceptional";
+      break;
+    case tcc_constant:
+      classtxt = "tcc_constant";
+      break;
+    case tcc_type:
+      classtxt = "tcc_type";
+      break;
+    case tcc_declaration:
+      classtxt = "tcc_declaration";
+      break;
+    case tcc_reference:
+      classtxt = "tcc_reference";
+      break;
+    case tcc_comparison:
+      classtxt = "tcc_comparison";
+      break;
+    case tcc_unary:
+      classtxt = "tcc_unary";
+      break;
+    case tcc_binary:
+      classtxt = "tcc_binary";
+      break;
+    case tcc_statement:
+      classtxt = "tcc_statement";
+      break;
+    case tcc_vl_exp:
+      classtxt = "tcc_vl_exp";
+      break;
+    case tcc_expression:
+      classtxt = "tcc_expression";
+      break;
+    default:
+      gcc_unreachable();
+      break;
+    }
+  rjd_fprintf("tree_code_class: %s\n", classtxt);
+  json_namevalue("tree_code_class", classtxt);
+
+  int required[64];
+  int processed[64];
+  for(int i=0; i<64; i++)
+    {
+    required[i] = CODE_CONTAINS_STRUCT(code, i);
+    processed[i] = 0;
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_BASE) )
+    {
+    processed[TS_BASE] = 1;
+    // There are 16 bits in TS_BASE.  The trouble is, they have
+    // different meanings for different codes; see the extensive
+    // comment in tree-core.h
+    strcpy(ach, "");
+    if( tclass != tcc_type && TREE_SIDE_EFFECTS(node) )
+      {
+      strcat(ach," side_effects");
+      }
+    if( tclass != tcc_type && TREE_CONSTANT(node) )
+      {
+      strcat(ach," constant");
+      }
+    if( TREE_ADDRESSABLE(node) )
+      {
+      strcat(ach," addressable");
+      }
+    if( TREE_THIS_VOLATILE(node) )
+      {
+      strcat(ach," volatile");
+      }
+    if( tclass != tcc_type && TREE_READONLY(node) )
+      {
+      strcat(ach," readonly");
+      }
+    if( TREE_ASM_WRITTEN(node) )
+      {
+      strcat(ach," asm_written");
+      }
+    if( TREE_NO_WARNING(node) )
+      {
+      strcat(ach," nowarning");
+      }
+    if( TREE_VISITED(node) )
+      {
+      strcat(ach," visited");
+      }
+
+    if( TREE_USED(node) )
+      {
+      strcat(ach," used");
+      }
+    if( TREE_NOTHROW(node) )
+      {
+      strcat(ach," nothrow");
+      }
+    if( TREE_STATIC(node) )
+      {
+      strcat(ach," static");
+      }
+    if( TREE_PUBLIC(node) )
+      {
+      strcat(ach," public");
+      }
+    if( TREE_PRIVATE(node) )
+      {
+      strcat(ach," private");
+      }
+    if( TREE_PROTECTED(node) )
+      {
+      strcat(ach," protected");
+      }
+    if( TREE_DEPRECATED(node) )
+      {
+      strcat(ach," deprecated");
+      }
+    if( node->base.default_def_flag ) //There isn't a TREE_DEFAULT_DEF
macro
+      {
+      strcat(ach," default_def");
+      }
+    if( tclass == tcc_constant )
+      {
+      if( TREE_OVERFLOW(node) )
+        {
+        strcat(ach," overflow");
+        }
+      }
+    if( tclass == tcc_type )
+      {
+      ADD_FLAG(TYPE_UNSIGNED,   "unsigned");
+      ADD_FLAG(TYPE_PACKED,     "packed");
+      ADD_FLAG(TYPE_USER_ALIGN, "user_align");
+      ADD_FLAG(TYPE_NAMELESS,   "nameless");
+      ADD_FLAG(TYPE_ATOMIC,     "atomic");
+      }
+    if( strlen(ach) )
+      {
+      rjd_fprintf("base_flags:%s\n", ach);
+      json_flags("base_flags", ach);
+      }
+    }
+
+  if( tclass == tcc_type )
+    {
+    sprintf(ach, "%s(%u)",
+            GET_MODE_NAME(TYPE_MODE(node)),(int)TYPE_MODE(node));
+    rjd_fprintf("machine_mode: %s\n", ach);
+    json_namevalue("machine_mode", ach);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_TYPED) )
+    {
+    processed[TS_TYPED] = 1;
+    rjd_subtree("type", TREE_TYPE(node));
+    if( tclass == tcc_type )
+      {
+      sprintf(ach, "%u", (int)TYPE_ADDR_SPACE(node));
+      rjd_fprintf("address_space:%s\n", ach);
+      json_namevalue("address_space", ach);
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_DECL_MINIMAL) )
+    {
+    processed[TS_DECL_MINIMAL] = 1;
+    rjd_subtree("name", DECL_NAME(node));
+    rjd_subtree("context", DECL_CONTEXT(node));
+    xloc = expand_location(DECL_SOURCE_LOCATION(node));
+    if( xloc.file )
+      {
+      sprintf(ach, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
+      rjd_fprintf("source_location: %s\n", ach);
+      json_namevalue("source_location", ach);
+      }
+    sprintf(ach, "%d", DECL_PT_UID(node));
+    rjd_fprintf("uid: %s\n", ach);
+    json_namevalue("uid", ach, NOT_QUOTED);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_DECL_COMMON) )
+    {
+    processed[TS_DECL_COMMON] = 1;
+    rjd_subtree("size(in bits)", DECL_SIZE(node));
+    rjd_subtree("size_unit(in bytes)", DECL_SIZE_UNIT(node));
+
+    const char *p;
+    switch(code)
+      {
+      case FUNCTION_DECL:
+        p = "initial(bindings)";
+        break;
+      case TRANSLATION_UNIT_DECL:
+        p = "initial(block)";
+        break;
+      case VAR_DECL:
+        p = "initial value";
+        break;
+      default:
+        p = "initial";
+        break;
+      }
+    rjd_subtree(p, DECL_INITIAL(node));
+    rjd_subtree("attributes", DECL_ATTRIBUTES(node));
+    rjd_subtree("abstract_origin", DECL_ABSTRACT_ORIGIN(node));
+
+    sprintf(ach, "%s(%u)",
GET_MODE_NAME(DECL_MODE(node)),(int)DECL_MODE(node));
+    rjd_fprintf("machine_mode: %s\n", ach);
+    json_namevalue("machine_mode", ach);
+
+    strcpy(ach, "");
+    if( DECL_NONLOCAL(node) )
+      {
+      strcat(ach," nonlocal");
+      }
+    if( DECL_VIRTUAL_P(node) )
+      {
+      strcat(ach," virtual");
+      }
+    if( DECL_IGNORED_P(node) )
+      {
+      strcat(ach," ignored");
+      }
+    if( DECL_ABSTRACT_P(node) )
+      {
+      strcat(ach," abstrac");
+      }
+    if( DECL_ARTIFICIAL(node) )
+      {
+      strcat(ach," artificial");
+      }
+    if( DECL_PRESERVE_P(node) )
+      {
+      strcat(ach," preserve");
+      }
+    if( code == VAR_DECL && DECL_DEBUG_EXPR(node) )
+      {
+      strcat(ach," debug_expr_is_from");
+      }
+    if( strlen(ach) )
+      {
+      rjd_fprintf("decl_flags:%s\n",ach);
+      json_flags("decl_flags", ach);
+      }
+
+    if( code == FIELD_DECL )
+      {
+      sprintf(ach, "%u", (int)DECL_OFFSET_ALIGN(node));
+      rjd_fprintf("offset_align: %s\n", ach);
+      json_namevalue("offset_align", ach);
+      }
+    sprintf(ach, "%u", (int)DECL_ALIGN(node));
+    rjd_fprintf("align: %u\n", ach);
+    json_namevalue("align", ach, NOT_QUOTED);
+
+    sprintf(ach, "%u", (int)DECL_WARN_IF_NOT_ALIGN(node));
+    rjd_fprintf("warn_if_not_align: %s\n", ach);
+    json_namevalue("warn_if_not_align", ach, NOT_QUOTED);
+
+    sprintf(ach, "%u", (int)DECL_PT_UID(node));
+    rjd_fprintf("pt_uid: %s\n", ach);
+    json_namevalue("pt_uid", ach, NOT_QUOTED);
+
+    if( DECL_LANG_SPECIFIC(node) )
+      {
+      rjd_fprintf("lang_specific(pointer):
%p\n",DECL_LANG_SPECIFIC(node));
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WRTL) )
+    {
+    processed[TS_DECL_WRTL] = 1;
+    if( DECL_RTL_SET_P(node) )
+      {
+      rjd_fprintf("DECL_RTL for NODE has already been set\n");
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_DECL_WITH_VIS) )
+    {
+    processed[TS_DECL_WITH_VIS] = 1;
+    rjd_subtree("raw_assembler_name", DECL_ASSEMBLER_NAME_RAW(node));
+    if( DECL_LANG_SPECIFIC(node) )
+      {
+      rjd_fprintf("symtab_node(pointer): %p\n",DECL_LANG_SPECIFIC(node));
+      }
+    strcpy(ach, "");
+    if( code == VAR_DECL )
+      {
+      if( DECL_DEFER_OUTPUT(node) )
+        {
+        strcat(ach, " defer_output");
+        }
+      if( DECL_HARD_REGISTER(node) )
+        {
+        strcat(ach, " hard_register");
+        }
+      if( DECL_COMMON(node) )
+        {
+        strcat(ach, " common");
+        }
+      if( DECL_IN_TEXT_SECTION(node) )
+        {
+        strcat(ach, " in_text_section");
+        }
+      if( DECL_IN_CONSTANT_POOL(node) )
+        {
+        strcat(ach, " in_constant_pool");
+        }
+      if( DECL_DLLIMPORT_P(node) )
+        {
+        strcat(ach, " dllimport_flag");
+        }
+      }
+    if( DECL_WEAK(node) )
+      {
+      strcat(ach, " weak_flag");
+      }
+    if( DECL_SEEN_IN_BIND_EXPR_P(node) )
+      {
+      strcat(ach, " seen_in_bind_expr");
+      }
+    if( DECL_COMDAT(node) )
+      {
+      strcat(ach, " comdat_flag");
+      }
+    if( DECL_VISIBILITY_SPECIFIED(node) )
+      {
+      strcat(ach," visibility_specified");
+      }
+    if( code == VAR_DECL && DECL_HAS_INIT_PRIORITY_P(node) )
+      {
+      strcat(ach," init_priority_p");
+      }
+    if(  code == FUNCTION_DECL && DECL_FINAL_P(node) )
+      {
+      strcat(ach," final");
+      }
+    if(  code == FUNCTION_DECL && DECL_STATIC_CHAIN(node) )
+      {
+      strcat(ach," regdecl_flag");
+      }
+    if( strlen(ach) )
+      {
+      rjd_fprintf("decl_with_vis flags:%s\n",ach);
+      json_flags("decl_with_vis", ach);
+      }
+
+    const char *p = "???";
+    switch( DECL_VISIBILITY(node) )
+      {
+      case VISIBILITY_DEFAULT:
+        p = "default";
+        break;
+      case VISIBILITY_PROTECTED:
+        p = "protected";
+        break;
+      case VISIBILITY_HIDDEN:
+        p = "hidden";
+        break;
+      case VISIBILITY_INTERNAL:
+        p = "internal";
+        break;
+      }
+    rjd_fprintf("visibility: %s\n", p);
+    json_namevalue("visibility", p);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_DECL_NON_COMMON) )
+    {
+    processed[TS_DECL_NON_COMMON] = 1;
+    rjd_subtree("result", DECL_RESULT_FLD(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_FUNCTION_DECL) )
+    {
+    processed[TS_FUNCTION_DECL] = 1;
+    if( node->function_decl.f )
+      {
+      rjd_fprintf("function(pointer): %p\n",node->function_decl.f);
+      }
+
+    rjd_subtree("arguments", DECL_ARGUMENTS(node));
+    rjd_subtree("personality", DECL_FUNCTION_PERSONALITY(node));
+    rjd_subtree("function_specific_target",
+                              DECL_FUNCTION_SPECIFIC_TARGET(node));
+    rjd_subtree("function_specific_optimization",
+                              DECL_FUNCTION_SPECIFIC_OPTIMIZATION(node));
+    const char *p = "saved_tree";
+    if( code == FUNCTION_DECL )
+      {
+      p = "saved_tree(function_body)";
+      }
+    rjd_subtree(p, DECL_SAVED_TREE(node));
+    rjd_subtree("vindex", DECL_VINDEX(node));
+
+    rjd_fprintf("function_code: %d\n",node->function_decl.function_code);
+
+    switch(DECL_BUILT_IN_CLASS(node))
+      {
+      case NOT_BUILT_IN:
+        break;
+      case BUILT_IN_FRONTEND:
+        rjd_fprintf("built_in: frontend\n");
+        json_namevalue("built_in", "frontend");
+        break;
+      case BUILT_IN_MD:
+        rjd_fprintf("built_in: md\n");
+        json_namevalue("built_in", "md");
+        break;
+      case BUILT_IN_NORMAL:
+        rjd_fprintf("built_in: normal\n");
+        json_namevalue("built_in", "normal");
+        break;
+      }
+    switch(FUNCTION_DECL_DECL_TYPE(node))
+      {
+      case NONE:
+        break;
+      case OPERATOR_NEW:
+        rjd_fprintf("operator_new: 1\n");
+        json_namevalue("operator_new", "1", NOT_QUOTED);
+        break;
+      case OPERATOR_DELETE:
+        rjd_fprintf("operator_delete: 1\n");
+        json_namevalue("operator_delete", "1", NOT_QUOTED);
+        break;
+      case LAMBDA_FUNCTION:
+        rjd_fprintf("lambda_function: 1\n");
+        json_namevalue("lambda_function", "1", NOT_QUOTED);
+        break;
+      }
+
+    strcpy(ach, "");
+    ADD_FLAG(TREE_PUBLIC, "public");
+    ADD_FLAG(DECL_STATIC_CONSTRUCTOR, "static_ctor_flag");
+    ADD_FLAG(DECL_STATIC_DESTRUCTOR, "static_dtor_flag");
+    ADD_FLAG(DECL_UNINLINABLE, "uninlinable");
+    ADD_FLAG(DECL_POSSIBLY_INLINED, "possibly_inlined");
+    ADD_FLAG(DECL_IS_NOVOPS, "novops_flag");
+    ADD_FLAG(DECL_IS_RETURNS_TWICE, "returns_twice_flag");
+    ADD_FLAG(DECL_IS_MALLOC, "malloc_flag");
+    ADD_FLAG(DECL_DECLARED_INLINE_P, "declared_inline_flag");
+    ADD_FLAG(DECL_NO_INLINE_WARNING_P, "no_inline_warning_flag");
+    ADD_FLAG(DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT,
+             "no_instrument_function_entry_exit");
+    ADD_FLAG(DECL_NO_LIMIT_STACK, "no_limit_stack");
+    if( TREE_CODE(node) == FUNCTION_DECL )
+      {
+      ADD_FLAG(DECL_DISREGARD_INLINE_LIMITS, "disregard_inline_limits");
+      }
+    ADD_FLAG(DECL_PURE_P, "pure_flag");
+    ADD_FLAG(DECL_LOOPING_CONST_OR_PURE_P, "looping_const_or_pure_flag");
+    ADD_FLAG(DECL_HAS_DEBUG_ARGS_P, "has_debug_args_flag");
+    ADD_FLAG(DECL_FUNCTION_VERSIONED, "versioned_function");
+    ADD_FLAG(DECL_IS_REPLACEABLE_OPERATOR, "replaceable_operator");
+    if( strlen(ach) )
+      {
+      rjd_fprintf("function_flags:%s\n", ach);
+      json_flags("function_flags", ach);
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_COMMON) )
+    {
+    processed[TS_TYPE_COMMON] = 1;
+
+    rjd_subtree("size(in bits)", TYPE_SIZE(node));
+    rjd_subtree("size_unit(in bytes)", TYPE_SIZE_UNIT(node));
+    rjd_subtree("attributes", TYPE_ATTRIBUTES(node));
+
+    sprintf(ach, "%d", TYPE_UID(node));
+    rjd_fprintf("uid: %s\n", ach);
+    json_namevalue("uid", ach, NOT_QUOTED);
+
+    if( !VECTOR_TYPE_P(node) )
+      {
+      sprintf(ach, "%d", TYPE_PRECISION(node));
+      rjd_fprintf("precision: %s\n", ach);
+      json_namevalue("precision", ach, NOT_QUOTED);
+      }
+
+    sprintf(ach, "%d", TYPE_CONTAINS_PLACEHOLDER_INTERNAL(node));
+    rjd_fprintf("contains_placeholder: %d\n", ach);
+    json_namevalue("contains_placeholder", ach, NOT_QUOTED);
+
+    strcpy(ach, "");
+    ADD_FLAG(TYPE_NO_FORCE_BLK,"no_force_blk_flag");
+    ADD_FLAG(TYPE_NEEDS_CONSTRUCTING,"needs_constructing_flag");
+    if( code == RECORD_TYPE
+        || code == UNION_TYPE
+        || code == QUAL_UNION_TYPE )
+      {
+      ADD_FLAG(TYPE_TRANSPARENT_AGGR,"transparent_aggr_flag");
+      }
+    ADD_FLAG(TYPE_RESTRICT, "restrict_flag");
+    if( code == RECORD_TYPE
+        || code == UNION_TYPE
+        || code == QUAL_UNION_TYPE
+        || code == ARRAY_TYPE )
+      {
+      ADD_FLAG(TYPE_TYPELESS_STORAGE, "typeless_storage");
+      }
+    ADD_FLAG(TYPE_EMPTY_P, "empty_flag");
+    ADD_FLAG(TYPE_INDIVISIBLE_P, "indivisible_p");
+    if( strlen(ach) )
+      {
+      rjd_fprintf("type_common_flags:%s\n", ach);
+      json_flags("type_common_flags", ach);
+      }
+
+    sprintf(ach, "%d",  TYPE_ALIGN(node));
+    rjd_fprintf("align: %s\n", ach);
+    json_namevalue("align", ach);
+
+    sprintf(ach, "%d", TYPE_WARN_IF_NOT_ALIGN(node));
+    rjd_fprintf("warn_if_not_align: %s\n", ach);
+    json_namevalue("warn_if_not_align", ach);
+
+    sprintf(ach, "%d", TYPE_ALIAS_SET(node));
+    rjd_fprintf("alias_set_type: %s\n", ach);
+    json_namevalue("alias_set_type", ach);
+
+    rjd_subtree("pointer_to", TYPE_POINTER_TO(node));
+    rjd_subtree("reference_to", TYPE_REFERENCE_TO(node));
+
+    rjd_subtree("canonical", TYPE_CANONICAL(node));
+    rjd_subtree("next_variant", TYPE_NEXT_VARIANT(node));
+    rjd_subtree("main_variant", TYPE_MAIN_VARIANT(node));
+    rjd_subtree("context", TYPE_CONTEXT(node));
+    rjd_subtree("name", TYPE_REFERENCE_TO(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_NON_COMMON) )
+    {
+    processed[TS_TYPE_NON_COMMON] = 1;
+    rjd_subtree("values", TYPE_VALUES_RAW(node));
+    rjd_subtree("minval", TYPE_MIN_VALUE_RAW(node));
+    rjd_subtree("maxval", TYPE_MAX_VALUE_RAW(node));
+    rjd_subtree("lang_1", TYPE_LANG_SLOT_1(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_WITH_LANG_SPECIFIC) )
+    {
+    processed[TS_TYPE_WITH_LANG_SPECIFIC] = 1;
+    if( TYPE_LANG_SPECIFIC(node) )
+      {
+      rjd_fprintf("lang_type(pointer): %p\n",TYPE_LANG_SPECIFIC(node));
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_INT_CST) )
+    {
+    processed[TS_INT_CST] = 1;
+    if( phase == 2 )
+    rjd_fprintf("value: ");
+    print_dec(wi::to_wide(node), ach, TYPE_SIGN(TREE_TYPE(node)));
+    rjd_fprintf("%s" , ach);
+    rjd_fprintf("\n");
+    json_namevalue("value", ach, NOT_QUOTED);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_REAL_CST) )
+    {
+    bool not_quoted = false;
+    processed[TS_REAL_CST] = 1;
+
+    if(TREE_OVERFLOW(node))
+      {
+      strcpy(ach, " overflow ");
+      }
+    else
+      {
+      REAL_VALUE_TYPE d = TREE_REAL_CST(node);
+      if(REAL_VALUE_ISINF(d))
+        {
+        strcpy(ach, REAL_VALUE_NEGATIVE(d) ? " -Inf" : " Inf");
+        }
+      else if(REAL_VALUE_ISNAN(d))
+        {
+        /* Print a NaN in the format [-][Q]NaN[(significand[exponent])]
+         where significand is a hexadecimal string that starts with
+         the 0x prefix followed by 0 if the number is not canonical
+         and a non-zero digit if it is, and exponent is decimal.  */
+        sprintf(ach,
+                "%s%sNaN",
+                d.sign ? "-" : "",
+                d.signalling ? "S" : "Q");
+        }
+      else
+        {
+        real_to_decimal(ach, &d, sizeof(ach), 0, 1);
+        not_quoted = true;
+        }
+      }
+
+    rjd_fprintf("value: %s\n", ach);
+    json_namevalue("value", ach, not_quoted);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_VEC) )
+    {
+    processed[TS_VEC] = 1;
+
+    len = TREE_VEC_LENGTH(node);
+    if(len)
+      {
+      json_start_array("tree_vector_elements");
+      }
+    for(i = 0; i < len; i++)
+      {
+      if( TREE_VEC_ELT(node, i)  )
+        {
+        char temp[32];
+        sprintf(temp, "tree_vector_element[%d]", i);
+
+        rjd_subtree(temp, TREE_VEC_ELT(node, i));
+        json_comma = ",";
+        }
+      }
+    if(len)
+      {
+      json_finish_array();
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_EXP) )
+    {
+    processed[TS_EXP] = 1;
+    xloc = expand_location( EXPR_LOCATION(node) );
+    if( xloc.file )
+      {
+      sprintf(ach, "%s:%d:%d",
+                  xloc.file,
+                  xloc.line,
+                  xloc.column);
+      rjd_fprintf("expression_location %s\n", ach);
+      json_namevalue("expression_location", ach);
+      }
+    len = TREE_OPERAND_LENGTH(node);
+
+    tree node_vars = NULL_TREE;
+    tree node_body = NULL_TREE;
+    tree node_block = NULL_TREE;
+
+    if( len )
+      {
+      json_start_array("operands");
+      }
+    for(i = 0; i < len; i++)
+      {
+      char temp[32];
+      sprintf(temp, "operand[%d]", i);
+
+      if( code == BIND_EXPR )
+        {
+        switch(i)
+          {
+          // vars/body/block is the order they appear.  I modify that to
+          // vars/block/body because that's easier for me to deal with
when I
+          // manually graph the connections of the results
+          case 0:
+            node_vars = node;
+            continue;
+            break;
+          case 1:
+            node_body = node;
+            continue;
+            break;
+          case 2:
+            node_block = node;
+            continue;
+            break;
+          }
+        }
+      if( code == COMPONENT_REF )
+        {
+        switch(i)
+          {
+          case 0:
+            strcpy(temp,"struct/union");
+            break;
+          case 1:
+            strcpy(temp,"field");
+            break;
+          case 2:
+            strcpy(temp,"offset");
+            break;
+          }
+        }
+      rjd_subtree(temp, TREE_OPERAND(node, i));
+      json_comma = ",";
+      }
+    if( len )
+      {
+      json_finish_array();
+      }
+
+    // Here's where I output the block/vars/body in my preferred order
+    if(node_block)
+      {
+      rjd_subtree("block", TREE_OPERAND(node_block, 2));
+      }
+    if(node_vars)
+      {
+      rjd_subtree("vars", TREE_OPERAND(node_vars, 0));
+      }
+    if(node_body)
+      {
+      rjd_subtree("body", TREE_OPERAND(node_body, 1));
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_LIST) )
+    {
+    processed[TS_LIST] = 1;
+    rjd_subtree("purpose", TREE_PURPOSE(node));
+    rjd_subtree("value", TREE_VALUE(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_STATEMENT_LIST) )
+    {
+    processed[TS_STATEMENT_LIST] = 1;
+
+    // We have a linked list to walk:
+
+    int i = 0;
+    tree_statement_list_node *next = STATEMENT_LIST_HEAD(node);
+    if( next )
+      {
+      json_start_array("statement_list");
+      }
+    while( next )
+      {
+      sprintf(ach,"statement_list[%d]",i++);
+      rjd_subtree(ach, next->stmt);
+
+      // By rights, this next statement, while not illegal, should
+      // be regarded as immoral.
+      next = next->next;
+      json_comma = ",";
+      }
+    if( STATEMENT_LIST_HEAD(node) )
+      {
+      json_finish_array();
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_RESULT_DECL) )
+    {
+    processed[TS_RESULT_DECL] = 1;
+
+    strcpy(ach, "");
+    ADD_FLAG(DECL_BY_REFERENCE, "by_reference");
+    ADD_FLAG(DECL_NONSHAREABLE, "nonshareable");
+    ADD_FLAG(DECL_HAS_VALUE_EXPR_P, "has_value_expr");
+    ADD_FLAG(SSA_VAR_P, "ssa_name_is_possible");
+    if( strlen(ach) )
+      {
+      rjd_fprintf("result_flags:%s\n",ach);
+      json_flags("result_flags", ach);
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_STRING) )
+    {
+    // First, do the text/html string:
+    strcpy(ach, "");
+    char ach2[8];
+    processed[TS_STRING] = 1;
+    const char *p = TREE_STRING_POINTER(node);
+    int i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
+    while(--i >= 0)
+      {
+      char ch = *p++;
+      if(ch >= ' ' && ch < 127)
+        {
+        sprintf(ach2, "%c", ch);
+        }
+      else
+        {
+        sprintf(ach2, "\\%03o", ch & 0xFF);
+        }
+      strcat(ach, ach2);
+      }
+    rjd_fprintf("string: \"");
+    rjd_fprintf("%s\"\n", ach);
+
+    // Second, do the JSON string:
+    strcpy(ach, "");
+    p = TREE_STRING_POINTER(node);
+    i = TREE_STRING_LENGTH(node); // sizeof(), not strlen()
+    while(--i >= 0)
+      {
+      char ch = *p++;
+      strcpy(ach2, "");
+      switch( ch )
+        {
+        case '\"' :
+          strcpy(ach2, "\\\"");
+          break;
+        case '\\' :
+          strcpy(ach2, "\\\\");
+          break;
+        case '/' :
+          strcpy(ach2, "/");
+          break;
+        case '\b' :
+          strcpy(ach2, "\\b");
+          break;
+        case '\f' :
+          strcpy(ach2, "\\f");
+          break;
+        case '\n' :
+          strcpy(ach2, "\\n");
+          break;
+        case '\r' :
+          strcpy(ach2, "\\r");
+          break;
+        case '\t' :
+          strcpy(ach2, "\\t");
+          break;
+        default:
+        if(ch >= ' ' && ch < 127)
+          {
+          sprintf(ach2, "%c", ch);
+          }
+        else
+          {
+          sprintf(ach2, "\\u%4.4x", (unsigned int)(ch & 0xFF));
+          }
+        }
+      strcat(ach, ach2);
+      }
+    json_namevalue("string", ach);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_IDENTIFIER) )
+    {
+    processed[TS_IDENTIFIER] = 1;
+    rjd_fprintf("identifier: \"%s\"\n", IDENTIFIER_POINTER(node));
+    json_namevalue("identifier", IDENTIFIER_POINTER(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_BLOCK) )
+    {
+    processed[TS_BLOCK] = 1;
+
+    sprintf(ach, "%u", BLOCK_NUMBER(node));
+    rjd_fprintf("block_num: %u\n", ach);
+    json_namevalue("block_num", ach);
+
+    xloc = expand_location(BLOCK_SOURCE_LOCATION(node));
+    if( xloc.file )
+      {
+      rjd_fprintf(ach, "%s:%d:%d",
+                  xloc.file,
+                  xloc.line,
+                  xloc.column);
+      rjd_fprintf("block_location_start: %s\n", ach);
+      json_namevalue("block_location_start", ach);
+      }
+
+    xloc = expand_location(BLOCK_SOURCE_END_LOCATION(node));
+    if( xloc.file )
+      {
+      rjd_fprintf(ach, "%s:%d:%d",
+                  xloc.file,
+                  xloc.line,
+                  xloc.column);
+      rjd_fprintf("block_location_end: %s\n", ach);
+      json_namevalue("block_location_end", ach);
+      }
+
+    if( BLOCK_DIE(node) )
+      {
+      rjd_fprintf("DWARF lexical block(pointer) %p\n", BLOCK_DIE(node));
+      }
+
+    rjd_subtree("vars", BLOCK_VARS(node));
+
+    len = BLOCK_NUM_NONLOCALIZED_VARS(node);
+    if( len )
+      {
+      json_start_array("nonlocalized_vars");
+      }
+    for(i = 0; i < len; i++)
+      {
+      char temp[32];
+      sprintf(temp, "nonlocalized_var[%d]", i);
+      rjd_subtree(temp, BLOCK_NONLOCALIZED_VAR(node, i));
+      json_comma = ",";
+      }
+    if( len )
+      {
+      json_finish_array();
+      }
+
+    rjd_subtree("subblocks", BLOCK_SUBBLOCKS(node));
+    rjd_subtree("supercontext", BLOCK_SUPERCONTEXT(node));
+    rjd_subtree("abstract_origin", BLOCK_ABSTRACT_ORIGIN(node));
+    rjd_subtree("fragment_origin", BLOCK_FRAGMENT_ORIGIN(node));
+    rjd_subtree("fragment_chain", BLOCK_FRAGMENT_CHAIN(node));
+    rjd_subtree("chain", BLOCK_CHAIN(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
+    {
+    processed[TS_PARM_DECL] = 1;
+    // The only attribute unique to TS_PARM_DECL is rtx rtl.
+    // I might process it, if I knew what it was.  RJD 2021-01-29
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_FIELD_DECL) )
+    {
+    processed[TS_FIELD_DECL] = 1;
+    rjd_subtree("offset", DECL_FIELD_OFFSET(node));
+    rjd_subtree("bit_field_type", DECL_BIT_FIELD_TYPE(node));
+    rjd_subtree("qualifier", DECL_QUALIFIER(node));
+    rjd_subtree("bit_offset", DECL_FIELD_BIT_OFFSET(node));
+    rjd_subtree("fcontext", DECL_FCONTEXT(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_VAR_DECL) )
+    {
+    processed[TS_VAR_DECL] = 1;
+    strcpy(ach, "");
+    ADD_FLAG(TREE_PUBLIC,"public");
+    ADD_FLAG(DECL_IN_TEXT_SECTION,"in_text_section");
+    ADD_FLAG(DECL_IN_CONSTANT_POOL,"in_constant_pool");
+    ADD_FLAG(DECL_HARD_REGISTER,"hard_register");
+    ADD_FLAG(DECL_HAS_INIT_PRIORITY_P,"init_priority_p");
+    ADD_FLAG(DECL_HAS_DEBUG_EXPR_P,"debug_expr_is_from");
+    ADD_FLAG(VAR_DECL_IS_VIRTUAL_OPERAND,"is_virtual_operand");
+    ADD_FLAG(DECL_NONLOCAL_FRAME,"non_local_frame_structure");
+    ADD_FLAG(DECL_NONALIASED,"non_aliased");
+    if( strlen(ach) )
+      {
+      rjd_fprintf("var_decl_flags:%s\n",ach);
+      json_flags("var_decl_flags", ach);
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_TYPE_DECL) )
+    {
+    processed[TS_TYPE_DECL] = 1;
+    strcpy(ach, "");
+    ADD_FLAG(TREE_PUBLIC,"public");
+    ADD_FLAG(TYPE_DECL_SUPPRESS_DEBUG,"suppress_debug");
+    if( strlen(ach) )
+      {
+      rjd_fprintf("type_decl_flags:%s\n",ach);
+      json_flags("type_decl_flags", ach);
+      }
+    rjd_subtree("original_type", DECL_ORIGINAL_TYPE(node));
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_CONST_DECL) )
+    {
+    processed[TS_CONST_DECL] = 1;
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_TRANSLATION_UNIT_DECL) )
+    {
+    processed[TS_TRANSLATION_UNIT_DECL] = 1;
+    if( TRANSLATION_UNIT_LANGUAGE(node) )
+      {
+      rjd_fprintf("language: %s\n", TRANSLATION_UNIT_LANGUAGE(node));
+      json_namevalue("language", TRANSLATION_UNIT_LANGUAGE(node));
+      }
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_LABEL_DECL) )
+    {
+    processed[TS_LABEL_DECL] = 1;
+
+    strcpy(ach, "");
+    ADD_FLAG(TREE_ADDRESSABLE,"outside_stack_levels");
+    ADD_FLAG(FORCED_LABEL,"forced_label");
+    ADD_FLAG(FALLTHROUGH_LABEL_P,"fallthrough_allowed");
+    ADD_FLAG(SWITCH_BREAK_LABEL_P,"switch_break");
+    ADD_FLAG(DECL_NONLOCAL,"nonlocal_permitted");
+    if( strlen(ach) )
+      {
+      rjd_fprintf("label_decl_flags:%s\n",ach);
+      json_flags("label_decl_flags", ach);
+      }
+
+    rjd_subtree("label_context", DECL_CONTEXT(node));
+
+    sprintf(ach, "%d", LABEL_DECL_UID(node));
+    rjd_fprintf("label_uid: %s\n", ach);
+    json_namevalue("label_uid", ach);
+
+    sprintf(ach, "%d", EH_LANDING_PAD_NR(node));
+    rjd_fprintf("eh_landing_pad: %s\n", ach);
+    json_namevalue("eh_landing_pad", ach);
+    }
+
+  if( CODE_CONTAINS_STRUCT(code, TS_CONSTRUCTOR) )
+    {
+    processed[TS_CONSTRUCTOR] = 1;
+
+    if( CONSTRUCTOR_NO_CLEARING(node) )
+      {
+      rjd_fprintf("no_clearing: ON\n");
+      json_namevalue("no_clearing", "true", NOT_QUOTED);
+      }
+    else
+      {
+      rjd_fprintf("no_clearing: off\n");
+      json_namevalue("no_clearing", "false", NOT_QUOTED);
+      }
+
+    char temp[32];
+
+    if( CONSTRUCTOR_NELTS(node) )
+      {
+      json_start_array("constructor_elts");
+      }
+    for(size_t i=0; i<CONSTRUCTOR_NELTS(node); i++)
+      {
+      constructor_elt *c_elt = CONSTRUCTOR_ELT(node, i);
+
+      sprintf(temp, "constructor_elt[%d].index",(int)i);
+      rjd_subtree(temp, c_elt->index);
+
+      sprintf(temp, "constructor_elt[%d].value",(int)i);
+      rjd_subtree(temp, c_elt->value);
+
+      int node_number_i = find_node_in_nodes(c_elt->index);
+      sprintf(ach, "{\"constructor\":{\"index_node\":%d,",
node_number_i);
+      json_newline();
+      json_fprintf("%s", ach);
+      json_comma = ",";
+
+      int node_number_v = find_node_in_nodes(c_elt->value);
+      sprintf(ach, "\"value_node\":%d}}", node_number_v);
+      json_fprintf("%s", ach);
+      json_comma = ",";
+      }
+    if( CONSTRUCTOR_NELTS(node) )
+      {
+      json_finish_array();
+      }
+    }
+
+  // We put this next statement at the end, because it's nice when CHAIN
+  // is the last thing we see:
+  if( CODE_CONTAINS_STRUCT(code, TS_COMMON) )
+    {
+    processed[TS_COMMON] = 1;
+    rjd_subtree("chain", TREE_CHAIN(node));
+    }
+
+  for(int i=0; i<64; i++)
+    {
+    if( required[i] && !processed[i])
+      {
+      /*  Arriving here means that you have encountered a tree struct
+          that you don't know how to handle.  This happened to me just
+          now.  The error message I see is
+
+              structure 'parm decl': NOT PROCESSED!!!!
+
+          What to do?
+
+          Well, the first thing I do is look in gcc/treestruct.def for
+          "parm decl".  I find that text in this line:
+
+              DEFTREESTRUCT(TS_PARM_DECL, "parm decl")
+
+          This tells me that the tree struct's enum code is TS_PARM_DECL
+
+          Next, I look in gcc/tree-core.h for the text "TS_PARM_DECL". I
find
+          it in this line:
+
+              struct tree_parm_decl  GTY ((tag("TS_PARM_DECL")))
parm_decl;
+
+          This tells me the structure I need to handle is
"tree_parm_decl".
+          The declaration of that structure is found earlier in
gcc/tree-core.h
+
+              struct GTY(()) tree_parm_decl {
+              struct tree_decl_with_rtl common;
+              rtx incoming_rtl; };
+
+          That structure has a substructure "tree_decl_with_rtl", which
will
+          require  its one handler.  The structure has its own specific
+          attribute "rtx incoming_rtl", which we might, or might not want
to
+          use as a source of display information.  But, in any case, we
do need
+          to add code up above for handling TS_PARM_DECL structures.
You'll
+          find that code in the block starting with
+
+              if( CODE_CONTAINS_STRUCT(code, TS_PARM_DECL) )
+                  {...}
+
+          For each attribute in the TS_xxx that you are working with,
there is
+          an accessor macro in gcc/tree.h for getting at that data.  You
need
+          to be using those macros; there are a lot of hints and
information
+          about the attribute.  For example, by searching for
".incoming_rtl"
+          -- note the leading dot -- you find this commented entry in
+          gcc/tree.h:
+
+              /_* For PARM_DECL, holds an RTL for the stack slot or
register
+                 where the data was actually passed.  *_/
+              #define DECL_INCOMING_RTL(NODE) \
+               (PARM_DECL_CHECK(NODE)->parm_decl.incoming_rtl)
+
+          We are processing a PARM_DECL node, which means that the macro
+          invocation:
+
+              DECL_INCOMING_RTL(node)
+
+          will safely return "incoming_rtl", because the PARM_DECL_CHECK
macro
+          won't throw an error.
+
+          And that's how this error message is eliminated.
+          */
+
+      rjd_fprintf("structure \'%s\': ",ts_enum_names[i]);
+      rjd_fprintf("NOT PROCESSED!!!!\n" );
+      fprintf(stderr, "structure \'%s\': ",ts_enum_names[i]);
+      fprintf(stderr, "NOT PROCESSED!!!!\n" );
+      exit(1);
+      }
+    }
+
+  if( phase==2 && fhtml )
+    {
+    fprintf(fhtml, "</p>\n");
+    }
+}
+
+void
+dump_generic_nodes(const char *filename, tree root)
+{
+  /*  This function is called at the beginning of
gimplify_function_tree() in
+   *  gimplify.cc.  The 'root' parameter is supposed to be a func_decl
node.
+   *
+   *  When the flag_dump_generic_nodes flag is true, that means the
compiler was
+   *  invoked with -fdump-generic-nodes, and that means we jump into
action.
+   *
+   *  We create two files based on filename.  When filename is NULL, it
gets
+   *  replaced with the name of the function as extracted from the
func_decl
+   *  node.
+   *
+   *  filename.nodes is a text file.  filename.nodes.html has the same
+   *  information in a hypertext file with in-file links to referenced
nodes.
+   *
+   *  The output is a listing of all of the function's nodes and their
+   *  attributes.  Here is an example of the first two nodes of a typical
file:
+
+***********************************This is NodeNumber0
+(0x7f12e13b0d00) NodeNumber0
+tree_code: function_decl
+tree_code_class: tcc_declaration
+base_flags: static public
+type: NodeNumber1 function_type
+name: NodeNumber6410 identifier_node "main"
+context: NodeNumber107 translation_unit_decl "bigger.c"
+source_location: bigger.c:7:5
+uid: 3663
+initial(bindings): NodeNumber6411 block
+machine_mode: QI(15)
+align: 8
+warn_if_not_align: 0
+pt_uid: 3663
+raw_assembler_name: NodeNumber6410 identifier_node "main"
+visibility: default
+result: NodeNumber6412 result_decl
+function(pointer): 0x7f12e135d508
+arguments: NodeNumber6413 parm_decl "argc"
+saved_tree(function_body): NodeNumber6417 statement_list
+function_code: 0
+function_flags: public no_instrument_function_entry_exit
+***********************************This is NodeNumber1
+(0x7f12e13b3d20) NodeNumber1
+tree_code: function_type
+tree_code_class: tcc_type
+machine_mode: QI(15)
+type: NodeNumber2 integer_type
+address_space:0
+size(in bits): NodeNumber55 uint128 8
+size_unit(in bytes): NodeNumber12 uint64 1
+uid: 1515
+precision: 0
+contains_placeholder: 0
+align: 8
+warn_if_not_align: 0
+alias_set_type: -1
+canonical: NodeNumber1 function_type
+main_variant: NodeNumber1 function_type
+values: NodeNumber6408 tree_list
+***********************************This is NodeNumber3
+
+  */
+
+  if( flag_dump_generic_nodes )
+    {
+    GV_number_of_nodes = 0;
+    char achFilename[1024];
+    if( !filename )
+      {
+      // If, perchance, the root is a decl, use its
+      // name field as the root of the output file.  This means
+      // that each function_decl will result in its output file:
+      // main.tags, foo.tags, bar.tags, and so on.
+
+      if( DECL_P(root) )
+        {
+        filename = IDENTIFIER_POINTER(DECL_NAME(root));
+        }
+      }
+
+    if( filename )
+      {
+      // Find the root name:
+      const char *p1 = strrchr(filename,'/');
+      if( p1 )
+        {
+        p1 += 1;
+        }
+      else
+        {
+        p1 = filename;
+        }
+      strcpy(achFilename,filename);
+      char *p2 = strrchr(achFilename,'.');
+      if( p2 )
+        {
+        *p2 = '\0';
+        }
+      strcat(achFilename,".nodes");
+      ftext = fopen(achFilename, "w");
+      if( !ftext )
+          {
+          error("Unable to open %s", achFilename);
+          }
+      // For reasons I don't understand this setvbuf is necessary.
Without
+      // it the vfprintf calls here in this routine interfere with
+      // printf calls elsewhere.
+      setvbuf(ftext, NULL, _IONBF, 0);
+
+      strcat(achFilename,".html");
+      fhtml = fopen(achFilename, "w");
+      if( !ftext )
+          {
+          error("Unable to open %s", achFilename);
+          }
+      setvbuf(fhtml, NULL, _IONBF, 0);
+
+      char title[1024];
+      strcpy(title, "GENERIC for ");
+      strcat(title, filename);
+      strcat(title, "()");
+
+      html_boilerplate(title);
+
+      strcpy(achFilename,filename);
+      p2 = strrchr(achFilename,'.');
+      if( p2 )
+        {
+        *p2 = '\0';
+        }
+      strcat(achFilename,".json");
+      fjson = fopen(achFilename, "w");
+      if( !fjson )
+          {
+          error("Unable to open %s", achFilename);
+          }
+      // For reasons I don't understand this setvbuf is necessary.
Without
+      // it the vfprintf calls here in this routine interfere with
+      // printf calls elsewhere.
+      setvbuf(fjson, NULL, _IONBF, 0);
+      }
+    else
+      {
+      fprintf(  stderr,
+                "%s(): Needs either a filename or a DECL_NODE\n",
+                __func__);
+      gcc_assert(false);
+      }
+
+    if( ftext )
+      {
+      phase = 1;
+      rjd_print_node(root);
+
+      phase = 2;
+
+      // Here is the JSON "header"
+      json_level = 0;
+      json_comma = ",";
+      json_indent();
+      json_fprintf("{\n");
+      json_fprintf("\"version\":\"1.0\"");
+
+      json_newline();
+      json_fprintf("\"node_count\":%d", GV_number_of_nodes);
+
+      json_comma = ",";
+      json_newline();
+      json_fprintf("\"nodes\":\n");
+      json_fprintf("[");
+
+      json_level += 1;
+      json_comma = "";
+      for(int i=0; i<GV_number_of_nodes; i++)
+        {
+        json_newline();
+        json_comma = ",";
+        json_fprintf("{\n");
+        json_indent();
+        json_fprintf("\"node\":%d", i);
+
+        rjd_print_node(nodes[i].this_node);
+
+        json_fprintf("\n");
+        json_indent();
+        json_fprintf("}");
+        }
+      json_level -= 1;
+
+      // Here is the JSON "trailer, that ties off the header"
+      json_fprintf("\n");
+      json_fprintf("]\n");
+      json_fprintf("}\n");
+      json_level = 1;
+
+      fclose(ftext);
+      ftext = NULL;
+
+      html_boilerplate();
+      fclose(fhtml);
+      ftext = NULL;
+
+      fclose(fjson);
+      ftext = NULL;
+      }
+    }
+}
diff --git a/gcc/dump-generic-nodes.h b/gcc/dump-generic-nodes.h
new file mode 100644
index 00000000000..ffe18ce2aa5
--- /dev/null
+++ b/gcc/dump-generic-nodes.h
@@ -0,0 +1,26 @@ 
+/* Various declarations for language-independent pretty-print
subroutines.
+   Copyright (C) 2002-2024 Free Software Foundation, Inc.
+   Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_PRINT_GIMPLE_NODES_H
+#define GCC_PRINT_GIMPLE_NODES_H
+
+extern void dump_generic_nodes(const char *file_name, tree fndecl);
+
+#endif
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 7f79b3cc7e6..7d3c23bcb80 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -70,6 +70,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "omp-offload.h"
 #include "context.h"
 #include "tree-nested.h"
+#include "dump-generic-nodes.h"
 
 /* Hash set of poisoned variables in a bind expr.  */
 static hash_set<tree> *asan_poisoned_variables = NULL;
@@ -19232,6 +19233,8 @@  gimplify_function_tree (tree fndecl)
 
   gcc_assert (!gimple_body (fndecl));
 
+  dump_generic_nodes(NULL, fndecl);
+
   if (DECL_STRUCT_FUNCTION (fndecl))
     push_cfun (DECL_STRUCT_FUNCTION (fndecl));
   else