/* Target description support for GDB.

  Copyright (C) 2018-2024 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 "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);
}