/* Target description support for GDB.

   Copyright (C) 2018-2022 Free Software Foundation, Inc.

   This file is part of GDB.

   This program 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 of the License, or
   (at your option) any later version.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "common-defs.h"
#include "gdbsupport/tdesc.h"

tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_,
		      int regnum, int save_restore_, const char *group_,
		      int bitsize_, const char *type_)
  : name (name_), target_regnum (regnum),
    save_restore (save_restore_),
    group (group_ != NULL ? group_ : ""),
    bitsize (bitsize_),
    type (type_ != NULL ? type_ : "<unknown>")
{
  /* If the register's type is target-defined, look it up now.  We may not
     have easy access to the containing feature when we want it later.  */
  tdesc_type = tdesc_named_type (feature, type.c_str ());
}

/* Predefined types.  */
static tdesc_type_builtin tdesc_predefined_types[] =
{
  { "bool", TDESC_TYPE_BOOL },
  { "int8", TDESC_TYPE_INT8 },
  { "int16", TDESC_TYPE_INT16 },
  { "int32", TDESC_TYPE_INT32 },
  { "int64", TDESC_TYPE_INT64 },
  { "int128", TDESC_TYPE_INT128 },
  { "uint8", TDESC_TYPE_UINT8 },
  { "uint16", TDESC_TYPE_UINT16 },
  { "uint32", TDESC_TYPE_UINT32 },
  { "uint64", TDESC_TYPE_UINT64 },
  { "uint128", TDESC_TYPE_UINT128 },
  { "code_ptr", TDESC_TYPE_CODE_PTR },
  { "data_ptr", TDESC_TYPE_DATA_PTR },
  { "ieee_half", TDESC_TYPE_IEEE_HALF },
  { "ieee_single", TDESC_TYPE_IEEE_SINGLE },
  { "ieee_double", TDESC_TYPE_IEEE_DOUBLE },
  { "arm_fpa_ext", TDESC_TYPE_ARM_FPA_EXT },
  { "i387_ext", TDESC_TYPE_I387_EXT },
  { "bfloat16", TDESC_TYPE_BFLOAT16 }
};

void tdesc_feature::accept (tdesc_element_visitor &v) const
{
  v.visit_pre (this);

  for (const tdesc_type_up &type : types)
    type->accept (v);

  for (const tdesc_reg_up &reg : registers)
    reg->accept (v);

  v.visit_post (this);
}

bool tdesc_feature::operator== (const tdesc_feature &other) const
{
  if (name != other.name)
    return false;

  if (registers.size () != other.registers.size ())
    return false;

  for (int ix = 0; ix < registers.size (); ix++)
    {
      const tdesc_reg_up &reg1 = registers[ix];
      const tdesc_reg_up &reg2 = other.registers[ix];

      if (reg1 != reg2 && *reg1 != *reg2)
	return false;
      }

  if (types.size () != other.types.size ())
    return false;

  for (int ix = 0; ix < types.size (); ix++)
    {
      const tdesc_type_up &type1 = types[ix];
      const tdesc_type_up &type2 = other.types[ix];

      if (type1 != type2 && *type1 != *type2)
	return false;
    }

  return true;
}

/* Lookup a predefined type.  */

static struct tdesc_type *
tdesc_predefined_type (enum tdesc_type_kind kind)
{
  for (int ix = 0; ix < ARRAY_SIZE (tdesc_predefined_types); ix++)
    if (tdesc_predefined_types[ix].kind == kind)
      return &tdesc_predefined_types[ix];

  gdb_assert_not_reached ("bad predefined tdesc type");
}

/* See gdbsupport/tdesc.h.  */

struct tdesc_type *
tdesc_named_type (const struct tdesc_feature *feature, const char *id)
{
  /* First try target-defined types.  */
  for (const tdesc_type_up &type : feature->types)
    if (type->name == id)
      return type.get ();

  /* Next try the predefined types.  */
  for (int ix = 0; ix < ARRAY_SIZE (tdesc_predefined_types); ix++)
    if (tdesc_predefined_types[ix].name == id)
      return &tdesc_predefined_types[ix];

  return NULL;
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_create_reg (struct tdesc_feature *feature, const char *name,
		  int regnum, int save_restore, const char *group,
		  int bitsize, const char *type)
{
  tdesc_reg *reg = new tdesc_reg (feature, name, regnum, save_restore,
				  group, bitsize, type);

  feature->registers.emplace_back (reg);
}

/* See gdbsupport/tdesc.h.  */

struct tdesc_type *
tdesc_create_vector (struct tdesc_feature *feature, const char *name,
		     struct tdesc_type *field_type, int count)
{
  tdesc_type_vector *type = new tdesc_type_vector (name, field_type, count);
  feature->types.emplace_back (type);

  return type;
}

/* See gdbsupport/tdesc.h.  */

tdesc_type_with_fields *
tdesc_create_struct (struct tdesc_feature *feature, const char *name)
{
  tdesc_type_with_fields *type
    = new tdesc_type_with_fields (name, TDESC_TYPE_STRUCT);
  feature->types.emplace_back (type);

  return type;
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_set_struct_size (tdesc_type_with_fields *type, int size)
{
  gdb_assert (type->kind == TDESC_TYPE_STRUCT);
  gdb_assert (size > 0);
  type->size = size;
}

/* See gdbsupport/tdesc.h.  */

tdesc_type_with_fields *
tdesc_create_union (struct tdesc_feature *feature, const char *name)
{
  tdesc_type_with_fields *type
    = new tdesc_type_with_fields (name, TDESC_TYPE_UNION);
  feature->types.emplace_back (type);

  return type;
}

/* See gdbsupport/tdesc.h.  */

tdesc_type_with_fields *
tdesc_create_flags (struct tdesc_feature *feature, const char *name,
		    int size)
{
  gdb_assert (size > 0);

  tdesc_type_with_fields *type
    = new tdesc_type_with_fields (name, TDESC_TYPE_FLAGS, size);
  feature->types.emplace_back (type);

  return type;
}

/* See gdbsupport/tdesc.h.  */

tdesc_type_with_fields *
tdesc_create_enum (struct tdesc_feature *feature, const char *name,
		   int size)
{
  gdb_assert (size > 0);

  tdesc_type_with_fields *type
    = new tdesc_type_with_fields (name, TDESC_TYPE_ENUM, size);
  feature->types.emplace_back (type);

  return type;
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_add_field (tdesc_type_with_fields *type, const char *field_name,
		 struct tdesc_type *field_type)
{
  gdb_assert (type->kind == TDESC_TYPE_UNION
	      || type->kind == TDESC_TYPE_STRUCT);

  /* Initialize start and end so we know this is not a bit-field
     when we print-c-tdesc.  */
  type->fields.emplace_back (field_name, field_type, -1, -1);
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_add_typed_bitfield (tdesc_type_with_fields *type, const char *field_name,
			  int start, int end, struct tdesc_type *field_type)
{
  gdb_assert (type->kind == TDESC_TYPE_STRUCT
	      || type->kind == TDESC_TYPE_FLAGS);
  gdb_assert (start >= 0 && end >= start);

  type->fields.emplace_back (field_name, field_type, start, end);
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_add_bitfield (tdesc_type_with_fields *type, const char *field_name,
		    int start, int end)
{
  struct tdesc_type *field_type;

  gdb_assert (start >= 0 && end >= start);

  if (type->size > 4)
    field_type = tdesc_predefined_type (TDESC_TYPE_UINT64);
  else
    field_type = tdesc_predefined_type (TDESC_TYPE_UINT32);

  tdesc_add_typed_bitfield (type, field_name, start, end, field_type);
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_add_flag (tdesc_type_with_fields *type, int start,
		const char *flag_name)
{
  gdb_assert (type->kind == TDESC_TYPE_FLAGS
	      || type->kind == TDESC_TYPE_STRUCT);

  type->fields.emplace_back (flag_name,
			     tdesc_predefined_type (TDESC_TYPE_BOOL),
			     start, start);
}

/* See gdbsupport/tdesc.h.  */

void
tdesc_add_enum_value (tdesc_type_with_fields *type, int value,
		      const char *name)
{
  gdb_assert (type->kind == TDESC_TYPE_ENUM);
  type->fields.emplace_back (name,
			     tdesc_predefined_type (TDESC_TYPE_INT32),
			     value, -1);
}

void print_xml_feature::visit_pre (const tdesc_feature *e)
{
  add_line ("<feature name=\"%s\">", e->name.c_str ());
  indent (1);
}

void print_xml_feature::visit_post (const tdesc_feature *e)
{
  indent (-1);
  add_line ("</feature>");
}

void print_xml_feature::visit (const tdesc_type_builtin *t)
{
  error (_("xml output is not supported for type \"%s\"."), t->name.c_str ());
}

void print_xml_feature::visit (const tdesc_type_vector *t)
{
  add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
	    t->name.c_str (), t->element_type->name.c_str (), t->count);
}

void print_xml_feature::visit (const tdesc_type_with_fields *t)
{
  const static char *types[] = { "struct", "union", "flags", "enum" };

  gdb_assert (t->kind >= TDESC_TYPE_STRUCT && t->kind <= TDESC_TYPE_ENUM);

  std::string tmp;

  string_appendf (tmp,
		  "<%s id=\"%s\"", types[t->kind - TDESC_TYPE_STRUCT],
		  t->name.c_str ());

  switch (t->kind)
    {
    case TDESC_TYPE_STRUCT:
    case TDESC_TYPE_FLAGS:
      if (t->size > 0)
	string_appendf (tmp, " size=\"%d\"", t->size);
      string_appendf (tmp, ">");
      add_line (tmp);

      for (const tdesc_type_field &f : t->fields)
	{
	  tmp.clear ();
	  string_appendf (tmp, "  <field name=\"%s\"", f.name.c_str ());
	  if (f.start != -1)
	    string_appendf (tmp, " start=\"%d\" end=\"%d\"", f.start,
			    f.end);
	  string_appendf (tmp, " type=\"%s\"/>",
			  f.type->name.c_str ());
	  add_line (tmp);
	}
      break;

    case TDESC_TYPE_ENUM:
      if (t->size > 0)
	string_appendf (tmp, " size=\"%d\"", t->size);
      string_appendf (tmp, ">");
      add_line (tmp);
      /* The 'start' of the field is reused as the enum value.  The 'end'
	 of the field is always set to -1 for enum values.  */
      for (const tdesc_type_field &f : t->fields)
	add_line ("  <evalue name=\"%s\" value=\"%d\"/>",
		  f.name.c_str (), f.start);
      break;

    case TDESC_TYPE_UNION:
      string_appendf (tmp, ">");
      add_line (tmp);
      for (const tdesc_type_field &f : t->fields)
	add_line ("  <field name=\"%s\" type=\"%s\"/>",
		  f.name.c_str (), f.type->name.c_str ());
      break;

    default:
      error (_("xml output is not supported for type \"%s\"."),
	     t->name.c_str ());
    }

  add_line ("</%s>", types[t->kind - TDESC_TYPE_STRUCT]);
}

void print_xml_feature::visit (const tdesc_reg *r)
{
  std::string tmp;

  string_appendf (tmp,
		  "<reg name=\"%s\" bitsize=\"%d\" type=\"%s\" regnum=\"%ld\"",
		  r->name.c_str (), r->bitsize, r->type.c_str (),
		  r->target_regnum);

  if (r->group.length () > 0)
    string_appendf (tmp, " group=\"%s\"", r->group.c_str ());

  if (r->save_restore == 0)
    string_appendf (tmp, " save-restore=\"no\"");

  string_appendf (tmp, "/>");

  add_line (tmp);
}

void print_xml_feature::visit_pre (const target_desc *e)
{
#ifndef IN_PROCESS_AGENT
  add_line ("<?xml version=\"1.0\"?>");
  add_line ("<!DOCTYPE target SYSTEM \"gdb-target.dtd\">");
  add_line ("<target>");
  indent (1);
  if (tdesc_architecture_name (e))
    add_line ("<architecture>%s</architecture>",
	      tdesc_architecture_name (e));

  const char *osabi = tdesc_osabi_name (e);
  if (osabi != nullptr)
    add_line ("<osabi>%s</osabi>", osabi);

  const std::vector<tdesc_compatible_info_up> &compatible_list
    = tdesc_compatible_info_list (e);
  for (const auto &c : compatible_list)
    add_line ("<compatible>%s</compatible>",
	      tdesc_compatible_info_arch_name (c));
#endif
}

void print_xml_feature::visit_post (const target_desc *e)
{
  indent (-1);
  add_line ("</target>");
}

/* See gdbsupport/tdesc.h.  */

void
print_xml_feature::add_line (const std::string &str)
{
  string_appendf (*m_buffer, "%*s", m_depth, "");
  string_appendf (*m_buffer, "%s", str.c_str ());
  string_appendf (*m_buffer, "\n");
}

/* See gdbsupport/tdesc.h.  */

void
print_xml_feature::add_line (const char *fmt, ...)
{
  std::string tmp;

  va_list ap;
  va_start (ap, fmt);
  string_vappendf (tmp, fmt, ap);
  va_end (ap);
  add_line (tmp);
}
