/* Code for GIMPLE range trace and debugging related routines.
   Copyright (C) 2019-2023 Free Software Foundation, Inc.
   Contributed by Andrew MacLeod <amacleod@redhat.com>
   and Aldy Hernandez <aldyh@redhat.com>.

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 "backend.h"
#include "tree.h"
#include "gimple.h"
#include "ssa.h"
#include "gimple-pretty-print.h"
#include "gimple-iterator.h"
#include "tree-cfg.h"
#include "fold-const.h"
#include "tree-cfg.h"
#include "cfgloop.h"
#include "tree-scalar-evolution.h"
#include "gimple-range.h"


// Breakpoint to trap at a specific index.  From GDB, this provides a simple
// place to put a breakpoint to stop at a given trace line.
// ie.  b range_tracer::breakpoint if index == 45678

void
range_tracer::breakpoint (unsigned index ATTRIBUTE_UNUSED)
{
}

// Construct a range_tracer with component NAME.

range_tracer::range_tracer (const char *name)
{
  gcc_checking_assert (strlen(name) < name_len -1);
  strcpy (component, name);
  indent = 0;
  tracing = false;
}

// This routine does the initial line spacing/indenting for a trace.
// If BLANKS is false, then IDX is printed, otherwise spaces.

void
range_tracer::print_prefix (unsigned idx, bool blanks)
{
  // Print counter index as well as INDENT spaces.
  if (!blanks)
    fprintf (dump_file, "%-7u ", idx);
  else
    fprintf (dump_file, "        ");
  fprintf (dump_file, "%s ", component);
  unsigned x;
  for (x = 0; x< indent; x++)
    fputc (' ', dump_file);

}
// If dumping, return the next call index and print the prefix for the next
// output line.  If not, return 0.
// Counter is static to monotonically increase across the compilation unit.

unsigned
range_tracer::do_header (const char *str)
{
  static unsigned trace_count = 0;

  unsigned idx = ++trace_count;
  print_prefix (idx, false);
  fprintf (dump_file, "%s", str);
  indent += bump;
  breakpoint (idx);
  return idx;
}

// Print a line without starting or ending a trace.

void
range_tracer::print (unsigned counter, const char *str)
{
  print_prefix (counter, true);
  fprintf (dump_file, "%s", str);
}

// End a trace and print the CALLER, NAME, and RESULT and range R,

void
range_tracer::trailer (unsigned counter, const char *caller, bool result,
		      tree name, const vrange &r)
{
  gcc_checking_assert (tracing && counter != 0);

  indent -= bump;
  print_prefix (counter, true);
  fputs(result ? "TRUE : " : "FALSE : ", dump_file);
  fprintf (dump_file, "(%u) ", counter);
  fputs (caller, dump_file);
  fputs (" (",dump_file);
  if (name)
    print_generic_expr (dump_file, name, TDF_SLIM);
  fputs (") ",dump_file);
  if (result)
    {
      r.dump (dump_file);
      fputc('\n', dump_file);
    }
  else
    fputc('\n', dump_file);
}

// =========================================
// Debugging helpers.
// =========================================

// Query all statements in the IL to precalculate computable ranges in RANGER.

DEBUG_FUNCTION void
debug_seed_ranger (gimple_ranger &ranger)
{
  // Recalculate SCEV to make sure the dump lists everything.
  if (scev_initialized_p ())
    {
      scev_finalize ();
      scev_initialize ();
    }

  basic_block bb;
  gimple_stmt_iterator gsi;
  FOR_EACH_BB_FN (bb, cfun)
    for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
      {
	gimple *stmt = gsi_stmt (gsi);

	if (is_gimple_debug (stmt))
	  continue;

	if (tree type = gimple_range_type (stmt))
	  {
	    Value_Range r (type);
	    ranger.range_of_stmt (r, stmt);
	  }
      }
}

// Change the current dump_file and dump_flags to F and FLAGS while
// saving them for later restoring.

push_dump_file::push_dump_file (FILE *f, dump_flags_t flags)
{
  old_dump_file = dump_file;
  old_dump_flags = dump_flags;
  dump_file = f;
  dump_flags = flags;
}

// Restore saved dump_file and dump_flags.

push_dump_file::~push_dump_file ()
{
  dump_file = old_dump_file;
  dump_flags = old_dump_flags;
}

// Dump all that ranger knows for the current function.

void
dump_ranger (FILE *out)
{
  push_dump_file save (out, dump_flags);
  gimple_ranger ranger;

  fprintf (out, ";; Function ");
  print_generic_expr (out, current_function_decl);
  fprintf (out, "\n");

  debug_seed_ranger (ranger);
  ranger.dump (out);
}

DEBUG_FUNCTION void
debug_ranger ()
{
  dump_ranger (stderr);
}

// Dump all that ranger knows on a path of BBs.
//
// Note that the blocks are in reverse order, thus the exit block is
// path[0].

void
dump_ranger (FILE *dump_file, const vec<basic_block> &path)
{
  if (path.length () == 0)
    {
      fprintf (dump_file, "empty\n");
      return;
    }

  gimple_ranger ranger;
  debug_seed_ranger (ranger);

  unsigned i = path.length ();
  do
    {
      i--;
      ranger.dump_bb (dump_file, path[i]);
    }
  while (i > 0);
}

DEBUG_FUNCTION void
debug_ranger (const vec<basic_block> &path)
{
  dump_ranger (stderr, path);
}

#include "gimple-range-tests.cc"