[PATCHv2,4/5] gdb/gdbarch: remove the 'invalid=None' state from gdbarch_components.py

Message ID 7452e65c89feea14efa71eb0272e00c2e68f6afd.1678116328.git.aburgess@redhat.com
State New
Headers
Series Add new gdbarch::displaced_step_buffer_length field |

Commit Message

Andrew Burgess March 6, 2023, 3:31 p.m. UTC
  This commit ensures that the 'invalid' property of all components is
either True, False, or a string.

Additionally, this commit allows a component to have both a predicate
and for the 'invalid' property to be a string.

Removing the option for 'invalid' to be None allows us to simplify the
algorithms in gdbarch.py a little.

Allowing a component to have both a predicate and an 'invalid' string
means that we can validate the value that a tdep sets into a field,
but also allow a predicate to ensure that the field has changed from
the default.

This functionality isn't going to be used in this series, but I have
tested it locally and believe that it would work, and this might make
it easier for others to add new components in the future.

In gdbarch_types.py, I've updated the type annotations to show that
the 'invalid' field should not be None, and I've changed the default
for this field from None to False.

Additionally, in gdbarch_types.py I've removed an assert from
Component.get_predicate.  This assert ensured that we didn't have the
predicate field set to True and the invalid field set to a string.
However, no component currently uses this configuration, and after
this commit, that combination is now supported, so the assert can be
removed.

As a consequence of the gdbarch_types.py changes we see some
additional comments generated in gdbarch.c about verification being
skipped due to the invalid field being False.

In gdbarch_components.py I've had to add 'invalid=True' for two
components: gcore_bfd_target and max_insn_length, without this the
validation in the gdbarch getter would disappear.

And in gdbarch.py I've reworked the logic for generating the
verify_gdbarch function, and for generating the getter functions.

The logic for generating the getter functions is still not ideal,  I
ended up having to add this additional logic block:

  elif c.postdefault is not None and c.predefault is not None:
      print("  /* Check variable changed from pre-default.  */", file=f)
      print(f"  gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f)

which was needed to ensure we continued to generate the same code as
before, without this the fact that invalid is now False when it would
previously have been None, meant that we dropped the gdb_assert in
favour of a comment like:

  print(f"  /* Skip verify of {c.name}, invalid_p == 0 */", file=f)

which is clearly not a good change.  We could potentially look at
improving this in a later commit, but I don't plan to do that in this
series.
---
 gdb/gdbarch.c             | 16 ++++++++++++++++
 gdb/gdbarch.py            | 32 +++++++++++++-------------------
 gdb/gdbarch_components.py |  2 ++
 gdb/gdbarch_types.py      |  7 +++----
 4 files changed, 34 insertions(+), 23 deletions(-)
  

Comments

Simon Marchi March 6, 2023, 8:13 p.m. UTC | #1
On 3/6/23 10:31, Andrew Burgess via Gdb-patches wrote:
> This commit ensures that the 'invalid' property of all components is
> either True, False, or a string.
> 
> Additionally, this commit allows a component to have both a predicate
> and for the 'invalid' property to be a string.
> 
> Removing the option for 'invalid' to be None allows us to simplify the
> algorithms in gdbarch.py a little.
> 
> Allowing a component to have both a predicate and an 'invalid' string
> means that we can validate the value that a tdep sets into a field,
> but also allow a predicate to ensure that the field has changed from
> the default.
> 
> This functionality isn't going to be used in this series, but I have
> tested it locally and believe that it would work, and this might make
> it easier for others to add new components in the future.
> 
> In gdbarch_types.py, I've updated the type annotations to show that
> the 'invalid' field should not be None, and I've changed the default
> for this field from None to False.
> 
> Additionally, in gdbarch_types.py I've removed an assert from
> Component.get_predicate.  This assert ensured that we didn't have the
> predicate field set to True and the invalid field set to a string.
> However, no component currently uses this configuration, and after
> this commit, that combination is now supported, so the assert can be
> removed.
> 
> As a consequence of the gdbarch_types.py changes we see some
> additional comments generated in gdbarch.c about verification being
> skipped due to the invalid field being False.
> 
> In gdbarch_components.py I've had to add 'invalid=True' for two
> components: gcore_bfd_target and max_insn_length, without this the
> validation in the gdbarch getter would disappear.

So, in my version of this, I gave `invalid` the default value True,
whereas you gave it the default False.  Upon further reflexion, I think
False is a good default.  It allows to set a predefault value and, if
the arch does not override it, that value is the effective value.  Seems
like a sane and simple default behavior.

This means you can remove all the `invalid=False,` from
gdbarch_components.py now (one find and replace).  I think you can tack
it as a separate patch on top to avoid noise, since you have other
changes in gdbarch_components.py already in this patch.

> @@ -1490,6 +1500,7 @@ const struct floatformat **
>  gdbarch_bfloat16_format (struct gdbarch *gdbarch)
>  {
>    gdb_assert (gdbarch != NULL);
> +  /* Skip verify of bfloat16_format, invalid_p == 0 */

I don't think the addition of these "Skip verify" comments in these
functions (that invoke the gdbarch functions and methods) make sense.

Simon
  
Tom Tromey March 7, 2023, 3:17 p.m. UTC | #2
>>>>> "Andrew" == Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:

Andrew> --- a/gdb/gdbarch_components.py
Andrew> +++ b/gdb/gdbarch_components.py
Andrew> @@ -1675,6 +1675,7 @@ BFD target to use when generating a core file.
Andrew>      predicate=True,
Andrew>      predefault="0",
Andrew>      printer="pstring (gdbarch->gcore_bfd_target)",
Andrew> +    invalid=True

Super nit, but it's better to have a trailing comma here, because it
means that future additions can be done without touching this line.

Tom
  
Simon Marchi March 7, 2023, 3:20 p.m. UTC | #3
On 3/7/23 10:17, Tom Tromey wrote:
>>>>>> "Andrew" == Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Andrew> --- a/gdb/gdbarch_components.py
> Andrew> +++ b/gdb/gdbarch_components.py
> Andrew> @@ -1675,6 +1675,7 @@ BFD target to use when generating a core file.
> Andrew>      predicate=True,
> Andrew>      predefault="0",
> Andrew>      printer="pstring (gdbarch->gcore_bfd_target)",
> Andrew> +    invalid=True
> 
> Super nit, but it's better to have a trailing comma here, because it
> means that future additions can be done without touching this line.

Actually, black adds it automatically in this case.

Simon
  

Patch

diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 7e4e34d5aca..efd111eeabc 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -298,29 +298,38 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of bfloat16_bit, invalid_p == 0 */
   if (gdbarch->bfloat16_format == 0)
     gdbarch->bfloat16_format = floatformats_bfloat16;
+  /* Skip verify of bfloat16_format, invalid_p == 0 */
   /* Skip verify of half_bit, invalid_p == 0 */
   if (gdbarch->half_format == 0)
     gdbarch->half_format = floatformats_ieee_half;
+  /* Skip verify of half_format, invalid_p == 0 */
   /* Skip verify of float_bit, invalid_p == 0 */
   if (gdbarch->float_format == 0)
     gdbarch->float_format = floatformats_ieee_single;
+  /* Skip verify of float_format, invalid_p == 0 */
   /* Skip verify of double_bit, invalid_p == 0 */
   if (gdbarch->double_format == 0)
     gdbarch->double_format = floatformats_ieee_double;
+  /* Skip verify of double_format, invalid_p == 0 */
   /* Skip verify of long_double_bit, invalid_p == 0 */
   if (gdbarch->long_double_format == 0)
     gdbarch->long_double_format = floatformats_ieee_double;
+  /* Skip verify of long_double_format, invalid_p == 0 */
   /* Skip verify of wchar_bit, invalid_p == 0 */
   if (gdbarch->wchar_signed == -1)
     gdbarch->wchar_signed = 1;
+  /* Skip verify of wchar_signed, invalid_p == 0 */
   /* Skip verify of floatformat_for_type, invalid_p == 0 */
   /* Skip verify of ptr_bit, invalid_p == 0 */
   if (gdbarch->addr_bit == 0)
     gdbarch->addr_bit = gdbarch_ptr_bit (gdbarch);
+  /* Skip verify of addr_bit, invalid_p == 0 */
   if (gdbarch->dwarf2_addr_size == 0)
     gdbarch->dwarf2_addr_size = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+  /* Skip verify of dwarf2_addr_size, invalid_p == 0 */
   if (gdbarch->char_signed == -1)
     gdbarch->char_signed = 1;
+  /* Skip verify of char_signed, invalid_p == 0 */
   /* Skip verify of read_pc, has predicate.  */
   /* Skip verify of write_pc, has predicate.  */
   /* Skip verify of virtual_frame_pointer, invalid_p == 0 */
@@ -412,6 +421,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of skip_trampoline_code, invalid_p == 0 */
   if (gdbarch->so_ops == 0)
     gdbarch->so_ops = &solib_target_so_ops;
+  /* Skip verify of so_ops, invalid_p == 0 */
   /* Skip verify of skip_solib_resolver, invalid_p == 0 */
   /* Skip verify of in_solib_return_trampoline, invalid_p == 0 */
   /* Skip verify of in_indirect_branch_thunk, invalid_p == 0 */
@@ -1490,6 +1500,7 @@  const struct floatformat **
 gdbarch_bfloat16_format (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
+  /* Skip verify of bfloat16_format, invalid_p == 0 */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_bfloat16_format called\n");
   return gdbarch->bfloat16_format;
@@ -1523,6 +1534,7 @@  const struct floatformat **
 gdbarch_half_format (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
+  /* Skip verify of half_format, invalid_p == 0 */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_half_format called\n");
   return gdbarch->half_format;
@@ -1556,6 +1568,7 @@  const struct floatformat **
 gdbarch_float_format (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
+  /* Skip verify of float_format, invalid_p == 0 */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_float_format called\n");
   return gdbarch->float_format;
@@ -1589,6 +1602,7 @@  const struct floatformat **
 gdbarch_double_format (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
+  /* Skip verify of double_format, invalid_p == 0 */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_double_format called\n");
   return gdbarch->double_format;
@@ -1622,6 +1636,7 @@  const struct floatformat **
 gdbarch_long_double_format (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
+  /* Skip verify of long_double_format, invalid_p == 0 */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_long_double_format called\n");
   return gdbarch->long_double_format;
@@ -3304,6 +3319,7 @@  const struct target_so_ops *
 gdbarch_so_ops (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
+  /* Skip verify of so_ops, invalid_p == 0 */
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_so_ops called\n");
   return gdbarch->so_ops;
diff --git a/gdb/gdbarch.py b/gdb/gdbarch.py
index 806aee26831..f296c160bbc 100755
--- a/gdb/gdbarch.py
+++ b/gdb/gdbarch.py
@@ -212,17 +212,12 @@  with open("gdbarch.c", "w") as f:
             print(f"    gdbarch->{c.name} = {c.postdefault};", file=f)
 
         # Now validate the value.
-        if c.invalid is False:
-            print(f"  /* Skip verify of {c.name}, invalid_p == 0 */", file=f)
-        elif c.predicate:
-            print(f"  /* Skip verify of {c.name}, has predicate.  */", file=f)
-        elif c.invalid is None:
-            # No validation has been requested for this component.
-            pass
-        elif isinstance(c.invalid, str):
+        if isinstance(c.invalid, str):
             print(f"  if ({c.invalid})", file=f)
             print(f"""    log.puts ("\\n\\t{c.name}");""", file=f)
-        elif c.invalid is True:
+        elif c.predicate:
+            print(f"  /* Skip verify of {c.name}, has predicate.  */", file=f)
+        elif c.invalid:
             if c.postdefault is not None:
                 # This component has its 'invalid' field set to True, but
                 # also has a postdefault.  This makes no sense, the
@@ -234,12 +229,7 @@  with open("gdbarch.c", "w") as f:
                 print(f"  if (gdbarch->{c.name} == {init_value})", file=f)
                 print(f"""    log.puts ("\\n\\t{c.name}");""", file=f)
         else:
-            # We should not allow ourselves to simply do nothing here
-            # because no other case applies.  If we end up here then
-            # either the input data needs adjusting so one of the
-            # above cases matches, or we need additional cases adding
-            # here.
-            raise Exception(f"unhandled case when generating gdbarch validation: {c.name}")
+            print(f"  /* Skip verify of {c.name}, invalid_p == 0 */", file=f)
     print("  if (!log.empty ())", file=f)
     print(
         """    internal_error (_("verify_gdbarch: the following are invalid ...%s"),""",
@@ -356,14 +346,18 @@  with open("gdbarch.c", "w") as f:
             print(f"gdbarch_{c.name} (struct gdbarch *gdbarch)", file=f)
             print("{", file=f)
             print("  gdb_assert (gdbarch != NULL);", file=f)
-            if c.invalid is False:
-                print(f"  /* Skip verify of {c.name}, invalid_p == 0 */", file=f)
-            elif isinstance(c.invalid, str):
+            if isinstance(c.invalid, str):
                 print("  /* Check variable is valid.  */", file=f)
                 print(f"  gdb_assert (!({c.invalid}));", file=f)
-            elif c.predefault:
+            elif c.postdefault is not None and c.predefault is not None:
                 print("  /* Check variable changed from pre-default.  */", file=f)
                 print(f"  gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f)
+            elif c.invalid:
+                if c.predefault:
+                    print("  /* Check variable changed from pre-default.  */", file=f)
+                    print(f"  gdb_assert (gdbarch->{c.name} != {c.predefault});", file=f)
+            else:
+                print(f"  /* Skip verify of {c.name}, invalid_p == 0 */", file=f)
             print("  if (gdbarch_debug >= 2)", file=f)
             print(
                 f"""    gdb_printf (gdb_stdlog, "gdbarch_{c.name} called\\n");""",
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 1f217123216..1eef2fb584e 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -1675,6 +1675,7 @@  BFD target to use when generating a core file.
     predicate=True,
     predefault="0",
     printer="pstring (gdbarch->gcore_bfd_target)",
+    invalid=True
 )
 
 Value(
@@ -1719,6 +1720,7 @@  The maximum length of an instruction on this architecture in bytes.
     name="max_insn_length",
     predicate=True,
     predefault="0",
+    invalid=True,
 )
 
 Method(
diff --git a/gdb/gdbarch_types.py b/gdb/gdbarch_types.py
index 988da80dd7c..d40851d127f 100755
--- a/gdb/gdbarch_types.py
+++ b/gdb/gdbarch_types.py
@@ -46,7 +46,7 @@  class Component:
         predicate: bool = False,
         predefault: Optional[str] = None,
         postdefault: Optional[str] = None,
-        invalid: Optional[Union[bool, str]] = None,
+        invalid: Union[bool, str] = False,
         params: Optional[List[Tuple[str, str]]] = None,
         param_checks: Optional[List[str]] = None,
         result_checks: Optional[List[str]] = None,
@@ -74,7 +74,6 @@  class Component:
 
     def get_predicate(self):
         "Return the expression used for validity checking."
-        assert self.predicate and not isinstance(self.invalid, str)
         if self.predefault:
             predicate = f"gdbarch->{self.name} != {self.predefault}"
         else:
@@ -98,7 +97,7 @@  class Value(Component):
         predicate: bool = False,
         predefault: Optional[str] = None,
         postdefault: Optional[str] = None,
-        invalid: Optional[Union[bool, str]] = None,
+        invalid: Union[bool, str] = False,
         printer: Optional[str] = None,
     ):
         super().__init__(
@@ -126,7 +125,7 @@  class Function(Component):
         predicate: bool = False,
         predefault: Optional[str] = None,
         postdefault: Optional[str] = None,
-        invalid: Optional[Union[bool, str]] = None,
+        invalid: Union[bool, str] = False,
         printer: Optional[str] = None,
         param_checks: Optional[List[str]] = None,
         result_checks: Optional[List[str]] = None,