mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 12:03:41 +08:00
18d2988e5d
Now that defs.h, server.h and common-defs.h are included via the `-include` option, it is no longer necessary for source files to include them. Remove all the inclusions of these files I could find. Update the generation scripts where relevant. Change-Id: Ia026cff269c1b7ae7386dd3619bc9bb6a5332837 Approved-By: Pedro Alves <pedro@palves.net>
1387 lines
42 KiB
C
1387 lines
42 KiB
C
/* Target-dependent code for the AMDGPU architectures.
|
|
|
|
Copyright (C) 2019-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 "amd-dbgapi-target.h"
|
|
#include "amdgpu-tdep.h"
|
|
#include "arch-utils.h"
|
|
#include "disasm.h"
|
|
#include "dwarf2/frame.h"
|
|
#include "frame-unwind.h"
|
|
#include "gdbarch.h"
|
|
#include "gdbsupport/selftest.h"
|
|
#include "gdbtypes.h"
|
|
#include "inferior.h"
|
|
#include "objfiles.h"
|
|
#include "observable.h"
|
|
#include "producer.h"
|
|
#include "reggroups.h"
|
|
|
|
/* See amdgpu-tdep.h. */
|
|
|
|
bool
|
|
is_amdgpu_arch (struct gdbarch *arch)
|
|
{
|
|
gdb_assert (arch != nullptr);
|
|
return gdbarch_bfd_arch_info (arch)->arch == bfd_arch_amdgcn;
|
|
}
|
|
|
|
/* See amdgpu-tdep.h. */
|
|
|
|
amdgpu_gdbarch_tdep *
|
|
get_amdgpu_gdbarch_tdep (gdbarch *arch)
|
|
{
|
|
return gdbarch_tdep<amdgpu_gdbarch_tdep> (arch);
|
|
}
|
|
|
|
/* Dummy implementation of gdbarch_return_value_as_value. */
|
|
|
|
static return_value_convention
|
|
amdgpu_return_value_as_value (gdbarch *arch, value *function, type *valtype,
|
|
regcache *regcache, value **read_value,
|
|
const gdb_byte *writebuf)
|
|
{
|
|
gdb_assert_not_reached ("not implemented");
|
|
}
|
|
|
|
/* Return the name of register REGNUM. */
|
|
|
|
static const char *
|
|
amdgpu_register_name (struct gdbarch *gdbarch, int regnum)
|
|
{
|
|
/* The list of registers reported by amd-dbgapi for a given architecture
|
|
contains some duplicate names. For instance, there is an "exec" register
|
|
for waves in the wave32 mode and one for the waves in the wave64 mode.
|
|
However, at most one register with a given name is actually allocated for
|
|
a specific wave. If INFERIOR_PTID represents a GPU wave, we query
|
|
amd-dbgapi to know whether the requested register actually exists for the
|
|
current wave, so there won't be duplicates in the the register names we
|
|
report for that wave.
|
|
|
|
But there are two known cases where INFERIOR_PTID doesn't represent a GPU
|
|
wave:
|
|
|
|
- The user does "set arch amdgcn:gfxNNN" followed with "maint print
|
|
registers"
|
|
- The "register_name" selftest
|
|
|
|
In these cases, we can't query amd-dbgapi to know whether we should hide
|
|
the register or not. The "register_name" selftest checks that there aren't
|
|
duplicates in the register names returned by the gdbarch, so if we simply
|
|
return all register names, that test will fail. The other simple option is
|
|
to never return a register name, which is what we do here. */
|
|
if (!ptid_is_gpu (inferior_ptid))
|
|
return "";
|
|
|
|
amd_dbgapi_wave_id_t wave_id = get_amd_dbgapi_wave_id (inferior_ptid);
|
|
amdgpu_gdbarch_tdep *tdep = get_amdgpu_gdbarch_tdep (gdbarch);
|
|
|
|
amd_dbgapi_register_exists_t register_exists;
|
|
if (amd_dbgapi_wave_register_exists (wave_id, tdep->register_ids[regnum],
|
|
®ister_exists)
|
|
!= AMD_DBGAPI_STATUS_SUCCESS
|
|
|| register_exists != AMD_DBGAPI_REGISTER_PRESENT)
|
|
return "";
|
|
|
|
return tdep->register_names[regnum].c_str ();
|
|
}
|
|
|
|
/* Return the internal register number for the DWARF register number DWARF_REG.
|
|
|
|
Return -1 if there's no internal register mapping to DWARF_REG. */
|
|
|
|
static int
|
|
amdgpu_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int dwarf_reg)
|
|
{
|
|
amdgpu_gdbarch_tdep *tdep = get_amdgpu_gdbarch_tdep (gdbarch);
|
|
|
|
if (dwarf_reg < tdep->dwarf_regnum_to_gdb_regnum.size ())
|
|
return tdep->dwarf_regnum_to_gdb_regnum[dwarf_reg];
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* A hierarchy of classes to represent an amd-dbgapi register type. */
|
|
|
|
struct amd_dbgapi_register_type
|
|
{
|
|
enum class kind
|
|
{
|
|
INTEGER,
|
|
FLOAT,
|
|
DOUBLE,
|
|
VECTOR,
|
|
CODE_PTR,
|
|
FLAGS,
|
|
ENUM,
|
|
};
|
|
|
|
amd_dbgapi_register_type (kind kind, std::string lookup_name)
|
|
: m_kind (kind), m_lookup_name (std::move (lookup_name))
|
|
{}
|
|
|
|
virtual ~amd_dbgapi_register_type () = default;
|
|
|
|
/* Return the type's kind. */
|
|
kind kind () const
|
|
{ return m_kind; }
|
|
|
|
/* Name to use for this type in the existing type map. */
|
|
const std::string &lookup_name () const
|
|
{ return m_lookup_name; }
|
|
|
|
private:
|
|
enum kind m_kind;
|
|
std::string m_lookup_name;
|
|
};
|
|
|
|
using amd_dbgapi_register_type_up = std::unique_ptr<amd_dbgapi_register_type>;
|
|
|
|
struct amd_dbgapi_register_type_integer : public amd_dbgapi_register_type
|
|
{
|
|
amd_dbgapi_register_type_integer (bool is_unsigned, unsigned int bit_size)
|
|
: amd_dbgapi_register_type
|
|
(kind::INTEGER,
|
|
string_printf ("%sint%d", is_unsigned ? "u" : "", bit_size)),
|
|
m_is_unsigned (is_unsigned),
|
|
m_bit_size (bit_size)
|
|
{}
|
|
|
|
bool is_unsigned () const
|
|
{ return m_is_unsigned; }
|
|
|
|
unsigned int bit_size () const
|
|
{ return m_bit_size; }
|
|
|
|
private:
|
|
bool m_is_unsigned;
|
|
unsigned int m_bit_size;
|
|
};
|
|
|
|
struct amd_dbgapi_register_type_float : public amd_dbgapi_register_type
|
|
{
|
|
amd_dbgapi_register_type_float ()
|
|
: amd_dbgapi_register_type (kind::FLOAT, "float")
|
|
{}
|
|
};
|
|
|
|
struct amd_dbgapi_register_type_double : public amd_dbgapi_register_type
|
|
{
|
|
amd_dbgapi_register_type_double ()
|
|
: amd_dbgapi_register_type (kind::DOUBLE, "double")
|
|
{}
|
|
};
|
|
|
|
struct amd_dbgapi_register_type_vector : public amd_dbgapi_register_type
|
|
{
|
|
amd_dbgapi_register_type_vector (const amd_dbgapi_register_type &element_type,
|
|
unsigned int count)
|
|
: amd_dbgapi_register_type (kind::VECTOR,
|
|
make_lookup_name (element_type, count)),
|
|
m_element_type (element_type),
|
|
m_count (count)
|
|
{}
|
|
|
|
const amd_dbgapi_register_type &element_type () const
|
|
{ return m_element_type; }
|
|
|
|
unsigned int count () const
|
|
{ return m_count; }
|
|
|
|
static std::string make_lookup_name
|
|
(const amd_dbgapi_register_type &element_type, unsigned int count)
|
|
{
|
|
return string_printf ("%s[%d]", element_type.lookup_name ().c_str (),
|
|
count);
|
|
}
|
|
|
|
private:
|
|
const amd_dbgapi_register_type &m_element_type;
|
|
unsigned int m_count;
|
|
};
|
|
|
|
struct amd_dbgapi_register_type_code_ptr : public amd_dbgapi_register_type
|
|
{
|
|
amd_dbgapi_register_type_code_ptr ()
|
|
: amd_dbgapi_register_type (kind::CODE_PTR, "void (*)()")
|
|
{}
|
|
};
|
|
|
|
struct amd_dbgapi_register_type_flags : public amd_dbgapi_register_type
|
|
{
|
|
struct field
|
|
{
|
|
std::string name;
|
|
unsigned int bit_pos_start;
|
|
unsigned int bit_pos_end;
|
|
const amd_dbgapi_register_type *type;
|
|
};
|
|
|
|
using container_type = std::vector<field>;
|
|
using const_iterator_type = container_type::const_iterator;
|
|
|
|
amd_dbgapi_register_type_flags (unsigned int bit_size, std::string_view name)
|
|
: amd_dbgapi_register_type (kind::FLAGS,
|
|
make_lookup_name (bit_size, name)),
|
|
m_bit_size (bit_size),
|
|
m_name (std::move (name))
|
|
{}
|
|
|
|
unsigned int bit_size () const
|
|
{ return m_bit_size; }
|
|
|
|
void add_field (std::string name, unsigned int bit_pos_start,
|
|
unsigned int bit_pos_end,
|
|
const amd_dbgapi_register_type *type)
|
|
{
|
|
m_fields.push_back (field {std::move (name), bit_pos_start,
|
|
bit_pos_end, type});
|
|
}
|
|
|
|
container_type::size_type size () const
|
|
{ return m_fields.size (); }
|
|
|
|
const field &operator[] (container_type::size_type pos) const
|
|
{ return m_fields[pos]; }
|
|
|
|
const_iterator_type begin () const
|
|
{ return m_fields.begin (); }
|
|
|
|
const_iterator_type end () const
|
|
{ return m_fields.end (); }
|
|
|
|
const std::string &name () const
|
|
{ return m_name; }
|
|
|
|
static std::string make_lookup_name (int bits, std::string_view name)
|
|
{
|
|
std::string res = string_printf ("flags%d_t ", bits);
|
|
res.append (name.data (), name.size ());
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
unsigned int m_bit_size;
|
|
container_type m_fields;
|
|
std::string m_name;
|
|
};
|
|
|
|
using amd_dbgapi_register_type_flags_up
|
|
= std::unique_ptr<amd_dbgapi_register_type_flags>;
|
|
|
|
struct amd_dbgapi_register_type_enum : public amd_dbgapi_register_type
|
|
{
|
|
struct enumerator
|
|
{
|
|
std::string name;
|
|
ULONGEST value;
|
|
};
|
|
|
|
using container_type = std::vector<enumerator>;
|
|
using const_iterator_type = container_type::const_iterator;
|
|
|
|
amd_dbgapi_register_type_enum (std::string_view name)
|
|
: amd_dbgapi_register_type (kind::ENUM, make_lookup_name (name)),
|
|
m_name (name.data (), name.length ())
|
|
{}
|
|
|
|
void set_bit_size (int bit_size)
|
|
{ m_bit_size = bit_size; }
|
|
|
|
unsigned int bit_size () const
|
|
{ return m_bit_size; }
|
|
|
|
void add_enumerator (std::string name, ULONGEST value)
|
|
{ m_enumerators.push_back (enumerator {std::move (name), value}); }
|
|
|
|
container_type::size_type size () const
|
|
{ return m_enumerators.size (); }
|
|
|
|
const enumerator &operator[] (container_type::size_type pos) const
|
|
{ return m_enumerators[pos]; }
|
|
|
|
const_iterator_type begin () const
|
|
{ return m_enumerators.begin (); }
|
|
|
|
const_iterator_type end () const
|
|
{ return m_enumerators.end (); }
|
|
|
|
const std::string &name () const
|
|
{ return m_name; }
|
|
|
|
static std::string make_lookup_name (std::string_view name)
|
|
{
|
|
std::string res = "enum ";
|
|
res.append (name.data (), name.length ());
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
unsigned int m_bit_size = 32;
|
|
container_type m_enumerators;
|
|
std::string m_name;
|
|
};
|
|
|
|
using amd_dbgapi_register_type_enum_up
|
|
= std::unique_ptr<amd_dbgapi_register_type_enum>;
|
|
|
|
/* Map type lookup names to types. */
|
|
using amd_dbgapi_register_type_map
|
|
= std::unordered_map<std::string, amd_dbgapi_register_type_up>;
|
|
|
|
/* Parse S as a ULONGEST, raise an error on overflow. */
|
|
|
|
static ULONGEST
|
|
try_strtoulst (std::string_view s)
|
|
{
|
|
errno = 0;
|
|
ULONGEST value = strtoulst (s.data (), nullptr, 0);
|
|
if (errno != 0)
|
|
error (_("Failed to parse integer."));
|
|
|
|
return value;
|
|
};
|
|
|
|
/* Shared regex bits. */
|
|
#define IDENTIFIER "[A-Za-z0-9_.]+"
|
|
#define WS "[ \t]+"
|
|
#define WSOPT "[ \t]*"
|
|
|
|
static const amd_dbgapi_register_type &
|
|
parse_amd_dbgapi_register_type (std::string_view type_name,
|
|
amd_dbgapi_register_type_map &type_map);
|
|
|
|
|
|
/* parse_amd_dbgapi_register_type helper for enum types. */
|
|
|
|
static void
|
|
parse_amd_dbgapi_register_type_enum_fields
|
|
(amd_dbgapi_register_type_enum &enum_type, std::string_view fields)
|
|
{
|
|
compiled_regex regex (/* name */
|
|
"^(" IDENTIFIER ")"
|
|
WSOPT "=" WSOPT
|
|
/* value */
|
|
"([0-9]+)"
|
|
WSOPT "(," WSOPT ")?",
|
|
REG_EXTENDED,
|
|
_("Error in AMDGPU enum register type regex"));
|
|
regmatch_t matches[4];
|
|
|
|
while (!fields.empty ())
|
|
{
|
|
int res = regex.exec (fields.data (), ARRAY_SIZE (matches), matches, 0);
|
|
if (res == REG_NOMATCH)
|
|
error (_("Failed to parse enum fields"));
|
|
|
|
auto sv_from_match = [fields] (const regmatch_t &m)
|
|
{ return fields.substr (m.rm_so, m.rm_eo - m.rm_so); };
|
|
|
|
std::string_view name = sv_from_match (matches[1]);
|
|
std::string_view value_str = sv_from_match (matches[2]);
|
|
ULONGEST value = try_strtoulst (value_str);
|
|
|
|
if (value > std::numeric_limits<uint32_t>::max ())
|
|
enum_type.set_bit_size (64);
|
|
|
|
enum_type.add_enumerator (std::string (name), value);
|
|
|
|
fields = fields.substr (matches[0].rm_eo);
|
|
}
|
|
}
|
|
|
|
/* parse_amd_dbgapi_register_type helper for flags types. */
|
|
|
|
static void
|
|
parse_amd_dbgapi_register_type_flags_fields
|
|
(amd_dbgapi_register_type_flags &flags_type,
|
|
int bits, std::string_view name, std::string_view fields,
|
|
amd_dbgapi_register_type_map &type_map)
|
|
{
|
|
gdb_assert (bits == 32 || bits == 64);
|
|
|
|
std::string regex_str
|
|
= string_printf (/* type */
|
|
"^(bool|uint%d_t|enum" WS IDENTIFIER WSOPT "(\\{[^}]*})?)"
|
|
WS
|
|
/* name */
|
|
"(" IDENTIFIER ")" WSOPT
|
|
/* bit position */
|
|
"@([0-9]+)(-[0-9]+)?" WSOPT ";" WSOPT,
|
|
bits);
|
|
compiled_regex regex (regex_str.c_str (), REG_EXTENDED,
|
|
_("Error in AMDGPU register type flags fields regex"));
|
|
regmatch_t matches[6];
|
|
|
|
while (!fields.empty ())
|
|
{
|
|
int res = regex.exec (fields.data (), ARRAY_SIZE (matches), matches, 0);
|
|
if (res == REG_NOMATCH)
|
|
error (_("Failed to parse flags type fields string"));
|
|
|
|
auto sv_from_match = [fields] (const regmatch_t &m)
|
|
{ return fields.substr (m.rm_so, m.rm_eo - m.rm_so); };
|
|
|
|
std::string_view field_type_str = sv_from_match (matches[1]);
|
|
std::string_view field_name = sv_from_match (matches[3]);
|
|
std::string_view pos_begin_str = sv_from_match (matches[4]);
|
|
ULONGEST pos_begin = try_strtoulst (pos_begin_str);
|
|
|
|
if (field_type_str == "bool")
|
|
flags_type.add_field (std::string (field_name), pos_begin, pos_begin,
|
|
nullptr);
|
|
else
|
|
{
|
|
if (matches[5].rm_so == -1)
|
|
error (_("Missing end bit position"));
|
|
|
|
std::string_view pos_end_str = sv_from_match (matches[5]);
|
|
ULONGEST pos_end = try_strtoulst (pos_end_str.substr (1));
|
|
const amd_dbgapi_register_type &field_type
|
|
= parse_amd_dbgapi_register_type (field_type_str, type_map);
|
|
flags_type.add_field (std::string (field_name), pos_begin, pos_end,
|
|
&field_type);
|
|
}
|
|
|
|
fields = fields.substr (matches[0].rm_eo);
|
|
}
|
|
}
|
|
|
|
/* parse_amd_dbgapi_register_type helper for scalars. */
|
|
|
|
static const amd_dbgapi_register_type &
|
|
parse_amd_dbgapi_register_type_scalar (std::string_view name,
|
|
amd_dbgapi_register_type_map &type_map)
|
|
{
|
|
std::string name_str (name);
|
|
auto it = type_map.find (name_str);
|
|
if (it != type_map.end ())
|
|
{
|
|
enum amd_dbgapi_register_type::kind kind = it->second->kind ();
|
|
if (kind != amd_dbgapi_register_type::kind::INTEGER
|
|
&& kind != amd_dbgapi_register_type::kind::FLOAT
|
|
&& kind != amd_dbgapi_register_type::kind::DOUBLE
|
|
&& kind != amd_dbgapi_register_type::kind::CODE_PTR)
|
|
error (_("type mismatch"));
|
|
|
|
return *it->second;
|
|
}
|
|
|
|
amd_dbgapi_register_type_up type;
|
|
if (name == "int32_t")
|
|
type.reset (new amd_dbgapi_register_type_integer (false, 32));
|
|
else if (name == "uint32_t")
|
|
type.reset (new amd_dbgapi_register_type_integer (true, 32));
|
|
else if (name == "int64_t")
|
|
type.reset (new amd_dbgapi_register_type_integer (false, 64));
|
|
else if (name == "uint64_t")
|
|
type.reset (new amd_dbgapi_register_type_integer (true, 64));
|
|
else if (name == "float")
|
|
type.reset (new amd_dbgapi_register_type_float ());
|
|
else if (name == "double")
|
|
type.reset (new amd_dbgapi_register_type_double ());
|
|
else if (name == "void (*)()")
|
|
type.reset (new amd_dbgapi_register_type_code_ptr ());
|
|
else
|
|
error (_("unknown type %s"), name_str.c_str ());
|
|
|
|
auto insertion_pair = type_map.emplace (name, std::move (type));
|
|
return *insertion_pair.first->second;
|
|
}
|
|
|
|
/* Parse an amd-dbgapi register type string into an amd_dbgapi_register_type
|
|
object.
|
|
|
|
See the documentation of AMD_DBGAPI_REGISTER_INFO_TYPE in amd-dbgapi.h for
|
|
details about the format. */
|
|
|
|
static const amd_dbgapi_register_type &
|
|
parse_amd_dbgapi_register_type (std::string_view type_str,
|
|
amd_dbgapi_register_type_map &type_map)
|
|
{
|
|
size_t pos_open_bracket = type_str.find_last_of ('[');
|
|
auto sv_from_match = [type_str] (const regmatch_t &m)
|
|
{ return type_str.substr (m.rm_so, m.rm_eo - m.rm_so); };
|
|
|
|
if (pos_open_bracket != std::string_view::npos)
|
|
{
|
|
/* Vector types. */
|
|
std::string_view element_type_str
|
|
= type_str.substr (0, pos_open_bracket);
|
|
const amd_dbgapi_register_type &element_type
|
|
= parse_amd_dbgapi_register_type (element_type_str, type_map);
|
|
|
|
size_t pos_close_bracket = type_str.find_last_of (']');
|
|
gdb_assert (pos_close_bracket != std::string_view::npos);
|
|
std::string_view count_str_view
|
|
= type_str.substr (pos_open_bracket + 1,
|
|
pos_close_bracket - pos_open_bracket);
|
|
std::string count_str (count_str_view);
|
|
unsigned int count = std::stoul (count_str);
|
|
|
|
std::string lookup_name
|
|
= amd_dbgapi_register_type_vector::make_lookup_name (element_type, count);
|
|
auto existing_type_it = type_map.find (lookup_name);
|
|
if (existing_type_it != type_map.end ())
|
|
{
|
|
gdb_assert (existing_type_it->second->kind ()
|
|
== amd_dbgapi_register_type::kind::VECTOR);
|
|
return *existing_type_it->second;
|
|
}
|
|
|
|
amd_dbgapi_register_type_up type
|
|
(new amd_dbgapi_register_type_vector (element_type, count));
|
|
auto insertion_pair
|
|
= type_map.emplace (type->lookup_name (), std::move (type));
|
|
return *insertion_pair.first->second;
|
|
}
|
|
|
|
if (type_str.find ("flags32_t") == 0 || type_str.find ("flags64_t") == 0)
|
|
{
|
|
/* Split 'type_str' into 4 tokens: "(type) (name) ({ (fields) })". */
|
|
compiled_regex regex ("^(flags32_t|flags64_t)"
|
|
WS "(" IDENTIFIER ")" WSOPT
|
|
"(\\{" WSOPT "(.*)})?",
|
|
REG_EXTENDED,
|
|
_("Error in AMDGPU register type regex"));
|
|
|
|
regmatch_t matches[5];
|
|
int res = regex.exec (type_str.data (), ARRAY_SIZE (matches), matches, 0);
|
|
if (res == REG_NOMATCH)
|
|
error (_("Failed to parse flags type string"));
|
|
|
|
std::string_view flags_keyword = sv_from_match (matches[1]);
|
|
unsigned int bit_size = flags_keyword == "flags32_t" ? 32 : 64;
|
|
std::string_view name = sv_from_match (matches[2]);
|
|
std::string lookup_name
|
|
= amd_dbgapi_register_type_flags::make_lookup_name (bit_size, name);
|
|
auto existing_type_it = type_map.find (lookup_name);
|
|
|
|
if (matches[3].rm_so == -1)
|
|
{
|
|
/* No braces, lookup existing type. */
|
|
if (existing_type_it == type_map.end ())
|
|
error (_("reference to unknown type %s."),
|
|
std::string (name).c_str ());
|
|
|
|
if (existing_type_it->second->kind ()
|
|
!= amd_dbgapi_register_type::kind::FLAGS)
|
|
error (_("type mismatch"));
|
|
|
|
return *existing_type_it->second;
|
|
}
|
|
else
|
|
{
|
|
/* With braces, it's a definition. */
|
|
if (existing_type_it != type_map.end ())
|
|
error (_("re-definition of type %s."),
|
|
std::string (name).c_str ());
|
|
|
|
amd_dbgapi_register_type_flags_up flags_type
|
|
(new amd_dbgapi_register_type_flags (bit_size, name));
|
|
std::string_view fields_without_braces = sv_from_match (matches[4]);
|
|
|
|
parse_amd_dbgapi_register_type_flags_fields
|
|
(*flags_type, bit_size, name, fields_without_braces, type_map);
|
|
|
|
auto insertion_pair
|
|
= type_map.emplace (flags_type->lookup_name (),
|
|
std::move (flags_type));
|
|
return *insertion_pair.first->second;
|
|
}
|
|
}
|
|
|
|
if (type_str.find ("enum") == 0)
|
|
{
|
|
compiled_regex regex ("^enum" WS "(" IDENTIFIER ")" WSOPT "(\\{" WSOPT "([^}]*)})?",
|
|
REG_EXTENDED,
|
|
_("Error in AMDGPU register type enum regex"));
|
|
|
|
/* Split 'type_name' into 3 tokens: "(name) ( { (fields) } )". */
|
|
regmatch_t matches[4];
|
|
int res = regex.exec (type_str.data (), ARRAY_SIZE (matches), matches, 0);
|
|
if (res == REG_NOMATCH)
|
|
error (_("Failed to parse flags type string"));
|
|
|
|
std::string_view name = sv_from_match (matches[1]);
|
|
|
|
std::string lookup_name
|
|
= amd_dbgapi_register_type_enum::make_lookup_name (name);
|
|
auto existing_type_it = type_map.find (lookup_name);
|
|
|
|
if (matches[2].rm_so == -1)
|
|
{
|
|
/* No braces, lookup existing type. */
|
|
if (existing_type_it == type_map.end ())
|
|
error (_("reference to unknown type %s"),
|
|
std::string (name).c_str ());
|
|
|
|
if (existing_type_it->second->kind ()
|
|
!= amd_dbgapi_register_type::kind::ENUM)
|
|
error (_("type mismatch"));
|
|
|
|
return *existing_type_it->second;
|
|
}
|
|
else
|
|
{
|
|
/* With braces, it's a definition. */
|
|
if (existing_type_it != type_map.end ())
|
|
error (_("re-definition of type %s"),
|
|
std::string (name).c_str ());
|
|
|
|
amd_dbgapi_register_type_enum_up enum_type
|
|
(new amd_dbgapi_register_type_enum (name));
|
|
std::string_view fields_without_braces = sv_from_match (matches[3]);
|
|
|
|
parse_amd_dbgapi_register_type_enum_fields
|
|
(*enum_type, fields_without_braces);
|
|
|
|
auto insertion_pair
|
|
= type_map.emplace (enum_type->lookup_name (),
|
|
std::move (enum_type));
|
|
return *insertion_pair.first->second;
|
|
}
|
|
}
|
|
|
|
return parse_amd_dbgapi_register_type_scalar (type_str, type_map);
|
|
}
|
|
|
|
/* Convert an amd_dbgapi_register_type object to a GDB type. */
|
|
|
|
static type *
|
|
amd_dbgapi_register_type_to_gdb_type (const amd_dbgapi_register_type &type,
|
|
struct gdbarch *gdbarch)
|
|
{
|
|
switch (type.kind ())
|
|
{
|
|
case amd_dbgapi_register_type::kind::INTEGER:
|
|
{
|
|
const auto &integer_type
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_integer &>
|
|
(type);
|
|
switch (integer_type.bit_size ())
|
|
{
|
|
case 32:
|
|
if (integer_type.is_unsigned ())
|
|
return builtin_type (gdbarch)->builtin_uint32;
|
|
else
|
|
return builtin_type (gdbarch)->builtin_int32;
|
|
|
|
case 64:
|
|
if (integer_type.is_unsigned ())
|
|
return builtin_type (gdbarch)->builtin_uint64;
|
|
else
|
|
return builtin_type (gdbarch)->builtin_int64;
|
|
|
|
default:
|
|
gdb_assert_not_reached ("invalid bit size");
|
|
}
|
|
}
|
|
|
|
case amd_dbgapi_register_type::kind::VECTOR:
|
|
{
|
|
const auto &vector_type
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_vector &>
|
|
(type);
|
|
struct type *element_type
|
|
= amd_dbgapi_register_type_to_gdb_type (vector_type.element_type (),
|
|
gdbarch);
|
|
return init_vector_type (element_type, vector_type.count ());
|
|
}
|
|
|
|
case amd_dbgapi_register_type::kind::FLOAT:
|
|
return builtin_type (gdbarch)->builtin_float;
|
|
|
|
case amd_dbgapi_register_type::kind::DOUBLE:
|
|
return builtin_type (gdbarch)->builtin_double;
|
|
|
|
case amd_dbgapi_register_type::kind::CODE_PTR:
|
|
return builtin_type (gdbarch)->builtin_func_ptr;
|
|
|
|
case amd_dbgapi_register_type::kind::FLAGS:
|
|
{
|
|
const auto &flags_type
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_flags &>
|
|
(type);
|
|
struct type *gdb_type
|
|
= arch_flags_type (gdbarch, flags_type.name ().c_str (),
|
|
flags_type.bit_size ());
|
|
|
|
for (const auto &field : flags_type)
|
|
{
|
|
if (field.type == nullptr)
|
|
{
|
|
gdb_assert (field.bit_pos_start == field.bit_pos_end);
|
|
append_flags_type_flag (gdb_type, field.bit_pos_start,
|
|
field.name.c_str ());
|
|
}
|
|
else
|
|
{
|
|
struct type *field_type
|
|
= amd_dbgapi_register_type_to_gdb_type (*field.type, gdbarch);
|
|
gdb_assert (field_type != nullptr);
|
|
append_flags_type_field
|
|
(gdb_type, field.bit_pos_start,
|
|
field.bit_pos_end - field.bit_pos_start + 1,
|
|
field_type, field.name.c_str ());
|
|
}
|
|
}
|
|
|
|
return gdb_type;
|
|
}
|
|
|
|
case amd_dbgapi_register_type::kind::ENUM:
|
|
{
|
|
const auto &enum_type
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_enum &>
|
|
(type);
|
|
struct type *gdb_type
|
|
= (type_allocator (gdbarch)
|
|
.new_type (TYPE_CODE_ENUM, enum_type.bit_size (),
|
|
enum_type.name ().c_str ()));
|
|
|
|
gdb_type->alloc_fields (enum_type.size ());
|
|
gdb_type->set_is_unsigned (true);
|
|
|
|
for (size_t i = 0; i < enum_type.size (); ++i)
|
|
{
|
|
const auto &field = enum_type[i];
|
|
gdb_type->field (i).set_name (xstrdup (field.name.c_str ()));
|
|
gdb_type->field (i).set_loc_enumval (field.value);
|
|
}
|
|
|
|
return gdb_type;
|
|
}
|
|
|
|
default:
|
|
gdb_assert_not_reached ("unhandled amd_dbgapi_register_type kind");
|
|
}
|
|
}
|
|
|
|
static type *
|
|
amdgpu_register_type (struct gdbarch *gdbarch, int regnum)
|
|
{
|
|
amdgpu_gdbarch_tdep *tdep = get_amdgpu_gdbarch_tdep (gdbarch);
|
|
|
|
if (tdep->register_types[regnum] == nullptr)
|
|
{
|
|
/* This is done lazily (not at gdbarch initialization time), because it
|
|
requires access to builtin_type, which can't be used while the gdbarch
|
|
is not fully initialized. */
|
|
char *bytes;
|
|
amd_dbgapi_status_t status
|
|
= amd_dbgapi_register_get_info (tdep->register_ids[regnum],
|
|
AMD_DBGAPI_REGISTER_INFO_TYPE,
|
|
sizeof (bytes), &bytes);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error (_("Failed to get register type from amd-dbgapi"));
|
|
|
|
gdb::unique_xmalloc_ptr<char> bytes_holder (bytes);
|
|
amd_dbgapi_register_type_map type_map;
|
|
const amd_dbgapi_register_type ®ister_type
|
|
= parse_amd_dbgapi_register_type (bytes, type_map);
|
|
tdep->register_types[regnum]
|
|
= amd_dbgapi_register_type_to_gdb_type (register_type, gdbarch);
|
|
gdb_assert (tdep->register_types[regnum] != nullptr);
|
|
}
|
|
|
|
return tdep->register_types[regnum];
|
|
}
|
|
|
|
static int
|
|
amdgpu_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
|
|
const reggroup *group)
|
|
{
|
|
amdgpu_gdbarch_tdep *tdep = get_amdgpu_gdbarch_tdep (gdbarch);
|
|
|
|
auto it = tdep->register_class_map.find (group->name ());
|
|
if (it == tdep->register_class_map.end ())
|
|
return group == all_reggroup;
|
|
|
|
amd_dbgapi_register_class_state_t state;
|
|
if (amd_dbgapi_register_is_in_register_class (it->second,
|
|
tdep->register_ids[regnum],
|
|
&state)
|
|
!= AMD_DBGAPI_STATUS_SUCCESS)
|
|
return group == all_reggroup;
|
|
|
|
return (state == AMD_DBGAPI_REGISTER_CLASS_STATE_MEMBER
|
|
|| group == all_reggroup);
|
|
}
|
|
|
|
static int
|
|
amdgpu_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *)
|
|
{
|
|
return get_amdgpu_gdbarch_tdep (gdbarch)->breakpoint_instruction_size;
|
|
}
|
|
|
|
static const gdb_byte *
|
|
amdgpu_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size)
|
|
{
|
|
*size = kind;
|
|
return get_amdgpu_gdbarch_tdep (gdbarch)->breakpoint_instruction_bytes.get ();
|
|
}
|
|
|
|
struct amdgpu_frame_cache
|
|
{
|
|
CORE_ADDR base;
|
|
CORE_ADDR pc;
|
|
};
|
|
|
|
static amdgpu_frame_cache *
|
|
amdgpu_frame_cache (const frame_info_ptr &this_frame, void **this_cache)
|
|
{
|
|
if (*this_cache != nullptr)
|
|
return (struct amdgpu_frame_cache *) *this_cache;
|
|
|
|
struct amdgpu_frame_cache *cache
|
|
= FRAME_OBSTACK_ZALLOC (struct amdgpu_frame_cache);
|
|
(*this_cache) = cache;
|
|
|
|
cache->pc = get_frame_func (this_frame);
|
|
cache->base = 0;
|
|
|
|
return cache;
|
|
}
|
|
|
|
static void
|
|
amdgpu_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
|
|
frame_id *this_id)
|
|
{
|
|
struct amdgpu_frame_cache *cache
|
|
= amdgpu_frame_cache (this_frame, this_cache);
|
|
|
|
if (get_frame_type (this_frame) == INLINE_FRAME)
|
|
(*this_id) = frame_id_build (cache->base, cache->pc);
|
|
else
|
|
(*this_id) = outer_frame_id;
|
|
|
|
frame_debug_printf ("this_frame=%d, type=%d, this_id=%s",
|
|
frame_relative_level (this_frame),
|
|
get_frame_type (this_frame),
|
|
this_id->to_string ().c_str ());
|
|
}
|
|
|
|
static frame_id
|
|
amdgpu_dummy_id (struct gdbarch *gdbarch, const frame_info_ptr &this_frame)
|
|
{
|
|
return frame_id_build (0, get_frame_pc (this_frame));
|
|
}
|
|
|
|
static struct value *
|
|
amdgpu_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
|
|
int regnum)
|
|
{
|
|
return frame_unwind_got_register (this_frame, regnum, regnum);
|
|
}
|
|
|
|
static const frame_unwind amdgpu_frame_unwind = {
|
|
"amdgpu",
|
|
NORMAL_FRAME,
|
|
default_frame_unwind_stop_reason,
|
|
amdgpu_frame_this_id,
|
|
amdgpu_frame_prev_register,
|
|
nullptr,
|
|
default_frame_sniffer,
|
|
nullptr,
|
|
nullptr,
|
|
};
|
|
|
|
static int
|
|
print_insn_amdgpu (bfd_vma memaddr, struct disassemble_info *info)
|
|
{
|
|
gdb_disassemble_info *di
|
|
= static_cast<gdb_disassemble_info *> (info->application_data);
|
|
|
|
/* Try to read at most INSTRUCTION_SIZE bytes. */
|
|
|
|
amd_dbgapi_size_t instruction_size = gdbarch_max_insn_length (di->arch ());
|
|
gdb::byte_vector buffer (instruction_size);
|
|
|
|
/* read_memory_func doesn't support partial reads, so if the read
|
|
fails, try one byte less, on and on until we manage to read
|
|
something. A case where this would happen is if we're trying to
|
|
read the last instruction at the end of a file section and that
|
|
instruction is smaller than the largest instruction. */
|
|
while (instruction_size > 0)
|
|
{
|
|
int ret = info->read_memory_func (memaddr, buffer.data (),
|
|
instruction_size, info);
|
|
if (ret == 0)
|
|
break;
|
|
|
|
--instruction_size;
|
|
}
|
|
|
|
if (instruction_size == 0)
|
|
{
|
|
info->memory_error_func (-1, memaddr, info);
|
|
return -1;
|
|
}
|
|
|
|
amd_dbgapi_architecture_id_t architecture_id;
|
|
amd_dbgapi_status_t status
|
|
= amd_dbgapi_get_architecture (gdbarch_bfd_arch_info (di->arch ())->mach,
|
|
&architecture_id);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
return -1;
|
|
|
|
auto symbolizer = [] (amd_dbgapi_symbolizer_id_t symbolizer_id,
|
|
amd_dbgapi_global_address_t address,
|
|
char **symbol_text) -> amd_dbgapi_status_t
|
|
{
|
|
gdb_disassemble_info *disasm_info
|
|
= reinterpret_cast<gdb_disassemble_info *> (symbolizer_id);
|
|
gdb_printing_disassembler *disasm
|
|
= dynamic_cast<gdb_printing_disassembler *> (disasm_info);
|
|
gdb_assert (disasm != nullptr);
|
|
|
|
string_file string (disasm->stream ()->can_emit_style_escape ());
|
|
print_address (disasm->arch (), address, &string);
|
|
*symbol_text = xstrdup (string.c_str ());
|
|
|
|
return AMD_DBGAPI_STATUS_SUCCESS;
|
|
};
|
|
auto symbolizer_id = reinterpret_cast<amd_dbgapi_symbolizer_id_t> (di);
|
|
char *instruction_text = nullptr;
|
|
status = amd_dbgapi_disassemble_instruction (architecture_id, memaddr,
|
|
&instruction_size,
|
|
buffer.data (),
|
|
&instruction_text,
|
|
symbolizer_id,
|
|
symbolizer);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
size_t alignment;
|
|
status = amd_dbgapi_architecture_get_info
|
|
(architecture_id,
|
|
AMD_DBGAPI_ARCHITECTURE_INFO_MINIMUM_INSTRUCTION_ALIGNMENT,
|
|
sizeof (alignment), &alignment);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error (_("amd_dbgapi_architecture_get_info failed"));
|
|
|
|
info->fprintf_func (di, "<illegal instruction>");
|
|
|
|
/* Skip to the next valid instruction address. */
|
|
return align_up (memaddr + 1, alignment) - memaddr;
|
|
}
|
|
|
|
/* Print the instruction. */
|
|
info->fprintf_func (di, "%s", instruction_text);
|
|
|
|
/* Free the memory allocated by the amd-dbgapi. */
|
|
xfree (instruction_text);
|
|
|
|
return static_cast<int> (instruction_size);
|
|
}
|
|
|
|
static CORE_ADDR
|
|
amdgpu_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
|
|
{
|
|
CORE_ADDR func_addr;
|
|
|
|
/* See if we can determine the end of the prologue via the symbol table.
|
|
If so, then return either PC, or the PC after the prologue, whichever
|
|
is greater. */
|
|
if (find_pc_partial_function (start_pc, nullptr, &func_addr, nullptr))
|
|
{
|
|
CORE_ADDR post_prologue_pc
|
|
= skip_prologue_using_sal (gdbarch, func_addr);
|
|
struct compunit_symtab *cust = find_pc_compunit_symtab (func_addr);
|
|
|
|
/* Clang always emits a line note before the prologue and another
|
|
one after. We trust clang to emit usable line notes. */
|
|
if (post_prologue_pc != 0
|
|
&& cust != nullptr
|
|
&& cust->producer () != nullptr
|
|
&& producer_is_llvm (cust->producer ()))
|
|
return std::max (start_pc, post_prologue_pc);
|
|
}
|
|
|
|
return start_pc;
|
|
}
|
|
|
|
static bool
|
|
amdgpu_supports_arch_info (const struct bfd_arch_info *info)
|
|
{
|
|
amd_dbgapi_architecture_id_t architecture_id;
|
|
amd_dbgapi_status_t status
|
|
= amd_dbgapi_get_architecture (info->mach, &architecture_id);
|
|
|
|
gdb_assert (status != AMD_DBGAPI_STATUS_ERROR_NOT_INITIALIZED);
|
|
return status == AMD_DBGAPI_STATUS_SUCCESS;
|
|
}
|
|
|
|
static struct gdbarch *
|
|
amdgpu_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
|
{
|
|
/* If there is already a candidate, use it. */
|
|
arches = gdbarch_list_lookup_by_info (arches, &info);
|
|
if (arches != nullptr)
|
|
return arches->gdbarch;
|
|
|
|
/* Allocate space for the new architecture. */
|
|
gdbarch_up gdbarch_u
|
|
(gdbarch_alloc (&info, gdbarch_tdep_up (new amdgpu_gdbarch_tdep)));
|
|
gdbarch *gdbarch = gdbarch_u.get ();
|
|
amdgpu_gdbarch_tdep *tdep = gdbarch_tdep<amdgpu_gdbarch_tdep> (gdbarch);
|
|
|
|
/* Data types. */
|
|
set_gdbarch_char_signed (gdbarch, 0);
|
|
set_gdbarch_ptr_bit (gdbarch, 64);
|
|
set_gdbarch_addr_bit (gdbarch, 64);
|
|
set_gdbarch_short_bit (gdbarch, 16);
|
|
set_gdbarch_int_bit (gdbarch, 32);
|
|
set_gdbarch_long_bit (gdbarch, 64);
|
|
set_gdbarch_long_long_bit (gdbarch, 64);
|
|
set_gdbarch_float_bit (gdbarch, 32);
|
|
set_gdbarch_double_bit (gdbarch, 64);
|
|
set_gdbarch_long_double_bit (gdbarch, 128);
|
|
set_gdbarch_half_format (gdbarch, floatformats_ieee_half);
|
|
set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
|
|
set_gdbarch_double_format (gdbarch, floatformats_ieee_double);
|
|
set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
|
|
|
|
/* Frame interpretation. */
|
|
set_gdbarch_skip_prologue (gdbarch, amdgpu_skip_prologue);
|
|
set_gdbarch_inner_than (gdbarch, core_addr_greaterthan);
|
|
dwarf2_append_unwinders (gdbarch);
|
|
frame_unwind_append_unwinder (gdbarch, &amdgpu_frame_unwind);
|
|
set_gdbarch_dummy_id (gdbarch, amdgpu_dummy_id);
|
|
|
|
/* Registers and memory. */
|
|
amd_dbgapi_architecture_id_t architecture_id;
|
|
amd_dbgapi_status_t status
|
|
= amd_dbgapi_get_architecture (gdbarch_bfd_arch_info (gdbarch)->mach,
|
|
&architecture_id);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("Failed to get architecture from amd-dbgapi"));
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
/* Add register groups. */
|
|
size_t register_class_count;
|
|
amd_dbgapi_register_class_id_t *register_class_ids;
|
|
status = amd_dbgapi_architecture_register_class_list (architecture_id,
|
|
®ister_class_count,
|
|
®ister_class_ids);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("Failed to get register class list from amd-dbgapi"));
|
|
return nullptr;
|
|
}
|
|
|
|
gdb::unique_xmalloc_ptr<amd_dbgapi_register_class_id_t>
|
|
register_class_ids_holder (register_class_ids);
|
|
|
|
for (size_t i = 0; i < register_class_count; ++i)
|
|
{
|
|
char *bytes;
|
|
status = amd_dbgapi_architecture_register_class_get_info
|
|
(register_class_ids[i], AMD_DBGAPI_REGISTER_CLASS_INFO_NAME,
|
|
sizeof (bytes), &bytes);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("Failed to get register class name from amd-dbgapi"));
|
|
return nullptr;
|
|
}
|
|
|
|
gdb::unique_xmalloc_ptr<char> name (bytes);
|
|
|
|
auto inserted = tdep->register_class_map.emplace (name.get (),
|
|
register_class_ids[i]);
|
|
gdb_assert (inserted.second);
|
|
|
|
/* Avoid creating a user reggroup with the same name as some built-in
|
|
reggroup, such as "general", "system", "vector", etc. */
|
|
if (reggroup_find (gdbarch, name.get ()) != nullptr)
|
|
continue;
|
|
|
|
/* Allocate the reggroup in the gdbarch. */
|
|
reggroup_add
|
|
(gdbarch, reggroup_gdbarch_new (gdbarch, name.get (), USER_REGGROUP));
|
|
}
|
|
|
|
/* Add registers. */
|
|
size_t register_count;
|
|
amd_dbgapi_register_id_t *register_ids;
|
|
status = amd_dbgapi_architecture_register_list (architecture_id,
|
|
®ister_count,
|
|
®ister_ids);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("Failed to get register list from amd-dbgapi"));
|
|
return nullptr;
|
|
}
|
|
|
|
gdb::unique_xmalloc_ptr<amd_dbgapi_register_id_t> register_ids_holder
|
|
(register_ids);
|
|
|
|
tdep->register_ids.insert (tdep->register_ids.end (), ®ister_ids[0],
|
|
®ister_ids[register_count]);
|
|
|
|
tdep->register_properties.resize (register_count,
|
|
AMD_DBGAPI_REGISTER_PROPERTY_NONE);
|
|
for (size_t regnum = 0; regnum < register_count; ++regnum)
|
|
{
|
|
auto ®ister_properties = tdep->register_properties[regnum];
|
|
if (amd_dbgapi_register_get_info (register_ids[regnum],
|
|
AMD_DBGAPI_REGISTER_INFO_PROPERTIES,
|
|
sizeof (register_properties),
|
|
®ister_properties)
|
|
!= AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("Failed to get register properties from amd-dbgapi"));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
set_gdbarch_num_regs (gdbarch, register_count);
|
|
set_gdbarch_num_pseudo_regs (gdbarch, 0);
|
|
|
|
tdep->register_names.resize (register_count);
|
|
tdep->register_types.resize (register_count);
|
|
for (size_t i = 0; i < register_count; ++i)
|
|
{
|
|
/* Set amd-dbgapi register id -> gdb regnum mapping. */
|
|
tdep->regnum_map.emplace (tdep->register_ids[i], i);
|
|
|
|
/* Get register name. */
|
|
char *bytes;
|
|
status = amd_dbgapi_register_get_info (tdep->register_ids[i],
|
|
AMD_DBGAPI_REGISTER_INFO_NAME,
|
|
sizeof (bytes), &bytes);
|
|
if (status == AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
tdep->register_names[i] = bytes;
|
|
xfree (bytes);
|
|
}
|
|
|
|
/* Get register DWARF number. */
|
|
uint64_t dwarf_num;
|
|
status = amd_dbgapi_register_get_info (tdep->register_ids[i],
|
|
AMD_DBGAPI_REGISTER_INFO_DWARF,
|
|
sizeof (dwarf_num), &dwarf_num);
|
|
if (status == AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
if (dwarf_num >= tdep->dwarf_regnum_to_gdb_regnum.size ())
|
|
tdep->dwarf_regnum_to_gdb_regnum.resize (dwarf_num + 1, -1);
|
|
|
|
tdep->dwarf_regnum_to_gdb_regnum[dwarf_num] = i;
|
|
}
|
|
}
|
|
|
|
amd_dbgapi_register_id_t pc_register_id;
|
|
status = amd_dbgapi_architecture_get_info
|
|
(architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_PC_REGISTER,
|
|
sizeof (pc_register_id), &pc_register_id);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
{
|
|
warning (_("Failed to get PC register from amd-dbgapi"));
|
|
return nullptr;
|
|
}
|
|
|
|
set_gdbarch_pc_regnum (gdbarch, tdep->regnum_map[pc_register_id]);
|
|
set_gdbarch_ps_regnum (gdbarch, -1);
|
|
set_gdbarch_sp_regnum (gdbarch, -1);
|
|
set_gdbarch_fp0_regnum (gdbarch, -1);
|
|
|
|
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, amdgpu_dwarf_reg_to_regnum);
|
|
|
|
set_gdbarch_return_value_as_value (gdbarch, amdgpu_return_value_as_value);
|
|
|
|
/* Register representation. */
|
|
set_gdbarch_register_name (gdbarch, amdgpu_register_name);
|
|
set_gdbarch_register_type (gdbarch, amdgpu_register_type);
|
|
set_gdbarch_register_reggroup_p (gdbarch, amdgpu_register_reggroup_p);
|
|
|
|
/* Disassembly. */
|
|
set_gdbarch_print_insn (gdbarch, print_insn_amdgpu);
|
|
|
|
/* Instructions. */
|
|
amd_dbgapi_size_t max_insn_length = 0;
|
|
status = amd_dbgapi_architecture_get_info
|
|
(architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_LARGEST_INSTRUCTION_SIZE,
|
|
sizeof (max_insn_length), &max_insn_length);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error (_("amd_dbgapi_architecture_get_info failed"));
|
|
|
|
set_gdbarch_max_insn_length (gdbarch, max_insn_length);
|
|
|
|
status = amd_dbgapi_architecture_get_info
|
|
(architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE,
|
|
sizeof (tdep->breakpoint_instruction_size),
|
|
&tdep->breakpoint_instruction_size);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error (_("amd_dbgapi_architecture_get_info failed"));
|
|
|
|
gdb_byte *breakpoint_instruction_bytes;
|
|
status = amd_dbgapi_architecture_get_info
|
|
(architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION,
|
|
sizeof (breakpoint_instruction_bytes), &breakpoint_instruction_bytes);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error (_("amd_dbgapi_architecture_get_info failed"));
|
|
|
|
tdep->breakpoint_instruction_bytes.reset (breakpoint_instruction_bytes);
|
|
|
|
set_gdbarch_breakpoint_kind_from_pc (gdbarch,
|
|
amdgpu_breakpoint_kind_from_pc);
|
|
set_gdbarch_sw_breakpoint_from_kind (gdbarch,
|
|
amdgpu_sw_breakpoint_from_kind);
|
|
|
|
amd_dbgapi_size_t pc_adjust;
|
|
status = amd_dbgapi_architecture_get_info
|
|
(architecture_id,
|
|
AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_PC_ADJUST,
|
|
sizeof (pc_adjust), &pc_adjust);
|
|
if (status != AMD_DBGAPI_STATUS_SUCCESS)
|
|
error (_("amd_dbgapi_architecture_get_info failed"));
|
|
|
|
set_gdbarch_decr_pc_after_break (gdbarch, pc_adjust);
|
|
|
|
return gdbarch_u.release ();
|
|
}
|
|
|
|
#if defined GDB_SELF_TEST
|
|
|
|
static void
|
|
amdgpu_register_type_parse_test ()
|
|
{
|
|
{
|
|
/* A type that exercises flags and enums, in particular looking up an
|
|
existing enum type by name. */
|
|
const char *flags_type_str =
|
|
"flags32_t mode { \
|
|
enum fp_round { \
|
|
NEAREST_EVEN = 0, \
|
|
PLUS_INF = 1, \
|
|
MINUS_INF = 2, \
|
|
ZERO = 3 \
|
|
} FP_ROUND.32 @0-1; \
|
|
enum fp_round FP_ROUND.64_16 @2-3; \
|
|
enum fp_denorm { \
|
|
FLUSH_SRC_DST = 0, \
|
|
FLUSH_DST = 1, \
|
|
FLUSH_SRC = 2, \
|
|
FLUSH_NONE = 3 \
|
|
} FP_DENORM.32 @4-5; \
|
|
enum fp_denorm FP_DENORM.64_16 @6-7; \
|
|
bool DX10_CLAMP @8; \
|
|
bool IEEE @9; \
|
|
bool LOD_CLAMPED @10; \
|
|
bool DEBUG_EN @11; \
|
|
bool EXCP_EN.INVALID @12; \
|
|
bool EXCP_EN.DENORM @13; \
|
|
bool EXCP_EN.DIV0 @14; \
|
|
bool EXCP_EN.OVERFLOW @15; \
|
|
bool EXCP_EN.UNDERFLOW @16; \
|
|
bool EXCP_EN.INEXACT @17; \
|
|
bool EXCP_EN.INT_DIV0 @18; \
|
|
bool EXCP_EN.ADDR_WATCH @19; \
|
|
bool FP16_OVFL @23; \
|
|
bool POPS_PACKER0 @24; \
|
|
bool POPS_PACKER1 @25; \
|
|
bool DISABLE_PERF @26; \
|
|
bool GPR_IDX_EN @27; \
|
|
bool VSKIP @28; \
|
|
uint32_t CSP @29-31; \
|
|
}";
|
|
amd_dbgapi_register_type_map type_map;
|
|
const amd_dbgapi_register_type &type
|
|
= parse_amd_dbgapi_register_type (flags_type_str, type_map);
|
|
|
|
gdb_assert (type.kind () == amd_dbgapi_register_type::kind::FLAGS);
|
|
|
|
const auto &f
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_flags &> (type);
|
|
gdb_assert (f.size () == 23);
|
|
|
|
/* Check the two "FP_ROUND" fields. */
|
|
auto check_fp_round_field
|
|
= [] (const char *name, const amd_dbgapi_register_type_flags::field &field)
|
|
{
|
|
gdb_assert (field.name == name);
|
|
gdb_assert (field.type->kind ()
|
|
== amd_dbgapi_register_type::kind::ENUM);
|
|
|
|
const auto &e
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_enum &>
|
|
(*field.type);
|
|
gdb_assert (e.size () == 4);
|
|
gdb_assert (e[0].name == "NEAREST_EVEN");
|
|
gdb_assert (e[0].value == 0);
|
|
gdb_assert (e[3].name == "ZERO");
|
|
gdb_assert (e[3].value == 3);
|
|
};
|
|
|
|
check_fp_round_field ("FP_ROUND.32", f[0]);
|
|
check_fp_round_field ("FP_ROUND.64_16", f[1]);
|
|
|
|
/* Check the "CSP" field. */
|
|
gdb_assert (f[22].name == "CSP");
|
|
gdb_assert (f[22].type->kind () == amd_dbgapi_register_type::kind::INTEGER);
|
|
|
|
const auto &i
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_integer &>
|
|
(*f[22].type);
|
|
gdb_assert (i.bit_size () == 32);
|
|
gdb_assert (i.is_unsigned ());
|
|
}
|
|
|
|
{
|
|
/* Test the vector type. */
|
|
const char *vector_type_str = "int32_t[64]";
|
|
amd_dbgapi_register_type_map type_map;
|
|
const amd_dbgapi_register_type &type
|
|
= parse_amd_dbgapi_register_type (vector_type_str, type_map);
|
|
|
|
gdb_assert (type.kind () == amd_dbgapi_register_type::kind::VECTOR);
|
|
|
|
const auto &v
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_vector &>
|
|
(type);
|
|
gdb_assert (v.count () == 64);
|
|
|
|
const auto &et = v.element_type ();
|
|
gdb_assert (et.kind () == amd_dbgapi_register_type::kind::INTEGER);
|
|
|
|
const auto &i
|
|
= gdb::checked_static_cast<const amd_dbgapi_register_type_integer &> (et);
|
|
gdb_assert (i.bit_size () == 32);
|
|
gdb_assert (!i.is_unsigned ());
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void _initialize_amdgpu_tdep ();
|
|
|
|
void
|
|
_initialize_amdgpu_tdep ()
|
|
{
|
|
gdbarch_register (bfd_arch_amdgcn, amdgpu_gdbarch_init, NULL,
|
|
amdgpu_supports_arch_info);
|
|
#if defined GDB_SELF_TEST
|
|
selftests::register_test ("amdgpu-register-type-parse-flags-fields",
|
|
amdgpu_register_type_parse_test);
|
|
#endif
|
|
}
|