| /* 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 "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 |
| } |