/* Pretty print support for value ranges.
   Copyright (C) 2019-2025 Free Software Foundation, Inc.
   Contributed by 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 "tree-pretty-print.h"
#include "fold-const.h"
#include "gimple-range.h"
#include "value-range-pretty-print.h"

static void
print_int_bound (pretty_printer *pp, const wide_int &bound, tree type)
{
  wide_int type_min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
  wide_int type_max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));

  if (INTEGRAL_TYPE_P (type)
      && !TYPE_UNSIGNED (type)
      && bound == type_min
      && TYPE_PRECISION (type) != 1)
    pp_string (pp, "-INF");
  else if (bound == type_max && TYPE_PRECISION (type) != 1)
    pp_string (pp, "+INF");
  else
    pp_wide_int (pp, bound, TYPE_SIGN (type));
}

static void
print_irange_bitmasks (pretty_printer *pp, const irange_bitmask &bm)
{
  if (bm.unknown_p ())
    return;

  pp_string (pp, " MASK ");
  char buf[WIDE_INT_PRINT_BUFFER_SIZE], *p;
  unsigned len_mask, len_val;
  if (print_hex_buf_size (bm.mask (), &len_mask)
      | print_hex_buf_size (bm.value (), &len_val))
    p = XALLOCAVEC (char, MAX (len_mask, len_val));
  else
    p = buf;
  print_hex (bm.mask (), p);
  pp_string (pp, p);
  pp_string (pp, " VALUE ");
  print_hex (bm.value (), p);
  pp_string (pp, p);
}

void
vrange_printer::visit (const unsupported_range &r) const
{
  pp_string (pp, "[unsupported_range] ");
  if (r.undefined_p ())
    {
      pp_string (pp, "UNDEFINED");
      return;
    }
  if (r.varying_p ())
    {
      pp_string (pp, "VARYING");
      return;
    }
  gcc_unreachable ();
}

void
vrange_printer::visit (const irange &r) const
{
  pp_string (pp, "[irange] ");
  if (r.undefined_p ())
    {
      pp_string (pp, "UNDEFINED");
      return;
    }
  dump_generic_node (pp, r.type (), 0, TDF_NONE | TDF_NOUID, false);
  pp_character (pp, ' ');
  if (r.varying_p ())
    {
      pp_string (pp, "VARYING");
      return;
    }
  for (unsigned i = 0; i < r.num_pairs (); ++i)
    {
      pp_character (pp, '[');
      print_int_bound (pp, r.lower_bound (i), r.type ());
      pp_string (pp, ", ");
      print_int_bound (pp, r.upper_bound (i), r.type ());
      pp_character (pp, ']');
    }
  print_irange_bitmasks (pp, r.m_bitmask);
}

void
vrange_printer::visit (const prange &r) const
{
  pp_string (pp, "[prange] ");
  if (r.undefined_p ())
    {
      pp_string (pp, "UNDEFINED");
      return;
    }
  dump_generic_node (pp, r.type (), 0, TDF_NONE | TDF_NOUID, false);
  pp_character (pp, ' ');
  if (r.varying_p ())
    {
      pp_string (pp, "VARYING");
      return;
    }

  pp_character (pp, '[');
  print_int_bound (pp, r.lower_bound (), r.type ());
  pp_string (pp, ", ");
  print_int_bound (pp, r.upper_bound (), r.type ());
  pp_character (pp, ']');
  print_irange_bitmasks (pp, r.m_bitmask);
}

void
vrange_printer::print_real_value (tree type, const REAL_VALUE_TYPE &r) const
{
  char s[100];
  real_to_decimal_for_mode (s, &r, sizeof (s), 0, 1, TYPE_MODE (type));
  pp_string (pp, s);
  if (!DECIMAL_FLOAT_TYPE_P (type)
      // real_to_hexadecimal prints infinities and NAN as text.  No
      // need to print them twice.
      && !real_isinf (&r)
      && !real_isnan (&r))
    {
      real_to_hexadecimal (s, &r, sizeof (s), 0, 1);
      pp_printf (pp, " (%s)", s);
    }
}

// Print an frange.

void
vrange_printer::visit (const frange &r) const
{
  pp_string (pp, "[frange] ");
  if (r.undefined_p ())
    {
      pp_string (pp, "UNDEFINED");
      return;
    }
  tree type = r.type ();
  dump_generic_node (pp, type, 0, TDF_NONE, false);
  pp_string (pp, " ");
  if (r.varying_p ())
    {
      pp_string (pp, "VARYING");
      print_frange_nan (r);
      return;
    }
  pp_character (pp, '[');
  bool has_endpoints = !r.known_isnan ();
  if (has_endpoints)
    {
      print_real_value (type, r.lower_bound ());
      pp_string (pp, ", ");
      print_real_value (type, r.upper_bound ());
    }
  pp_character (pp, ']');
  print_frange_nan (r);
}

// Print the NAN info for an frange.

void
vrange_printer::print_frange_nan (const frange &r) const
{
  if (r.maybe_isnan ())
    {
      if (r.m_pos_nan && r.m_neg_nan)
	{
	  pp_string (pp, " +-NAN");
	  return;
	}
      bool nan_sign = r.m_neg_nan;
      if (nan_sign)
	pp_string (pp, " -NAN");
      else
	pp_string (pp, " +NAN");
    }
}