[7/8] Use gdb_gmp for scalar arithmetic

Message ID 20230303211207.1053037-8-tromey@adacore.com
State New
Headers
Series Arithmetic for 128-bit types |

Commit Message

Tom Tromey March 3, 2023, 9:12 p.m. UTC
  This changes gdb to use scalar arithmetic for expression evaluation.

I suspect this patch is not truly complete, as there may be code paths
that still don't correctly handle 128-bit integers.  However, many
things do work now.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30190
---
 gdb/testsuite/gdb.rust/onetwoeight.exp |  65 ++++
 gdb/testsuite/gdb.rust/onetwoeight.rs  |  31 ++
 gdb/utils.c                            |  30 --
 gdb/utils.h                            |   7 -
 gdb/valarith.c                         | 489 ++++++++-----------------
 gdb/valops.c                           |  14 +-
 6 files changed, 253 insertions(+), 383 deletions(-)
 create mode 100644 gdb/testsuite/gdb.rust/onetwoeight.exp
 create mode 100644 gdb/testsuite/gdb.rust/onetwoeight.rs
  

Patch

diff --git a/gdb/testsuite/gdb.rust/onetwoeight.exp b/gdb/testsuite/gdb.rust/onetwoeight.exp
new file mode 100644
index 00000000000..317c848a899
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/onetwoeight.exp
@@ -0,0 +1,65 @@ 
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test expression parsing and evaluation that requires Rust compiler.
+
+load_lib rust-support.exp
+require allow_rust_tests
+
+set v [split [rust_compiler_version] .]
+if {[lindex $v 0] == 1 && [lindex $v 1] < 43} {
+    untested "128-bit ints require rust 1.43 or greater"
+    return -1
+}
+
+standard_testfile .rs
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug rust}]} {
+    return -1
+}
+
+set line [gdb_get_line_number "BREAK"]
+if {![runto ${srcfile}:$line]} {
+    untested "could not run to breakpoint"
+    return -1
+}
+
+gdb_test "print x" " = 340282366920938463463374607431768211455"
+gdb_test "print y" " = 170141183460469231731687303715884105727"
+
+gdb_test "print x / 2" " = 170141183460469231731687303715884105727"
+gdb_test "print sm * 2" " = 170141183460469231731687303715884105726"
+gdb_test "print sm + sm" " = 170141183460469231731687303715884105726"
+gdb_test "print x - y" " = 170141183460469231731687303715884105728"
+gdb_test "print -y" " = -170141183460469231731687303715884105727"
+gdb_test "print +y" " = 170141183460469231731687303715884105727"
+
+gdb_test "print/x x" " = 0xffffffffffffffffffffffffffffffff"
+gdb_test "print x % 4" " = 3"
+gdb_test "print !x" " = 0"
+
+gdb_test "print x < 0" " = false"
+gdb_test "print -y < 0" " = true"
+gdb_test "print x > y" " = true"
+gdb_test "print y >= y" " = true"
+gdb_test "print y <= y" " = true"
+gdb_test "print y == y" " = true"
+gdb_test "print x != y" " = true"
+
+gdb_test "print sm << 2" "= 340282366920938463463374607431768211452"
+gdb_test "print x >> 2" "= 85070591730234615865843651857942052863"
+
+gdb_test "print/x x & mask" " = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0"
+gdb_test "print/x x ^ mask" " = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
+gdb_test "print/x mask | (mask >> 4)" " = 0xffffffffffffffffffffffffffffffff"
diff --git a/gdb/testsuite/gdb.rust/onetwoeight.rs b/gdb/testsuite/gdb.rust/onetwoeight.rs
new file mode 100644
index 00000000000..aa8cb1cf7b1
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/onetwoeight.rs
@@ -0,0 +1,31 @@ 
+// Copyright (C) 2023 Free Software Foundation, Inc.
+
+// 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/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+
+fn empty() {
+}
+
+fn main () {
+    let x : u128 = 340_282_366_920_938_463_463_374_607_431_768_211_455;
+    let sm : u128 = x /4;
+    let y : i128 = 170_141_183_460_469_231_731_687_303_715_884_105_727;
+    let mask : u128 = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0;
+
+    empty();			// BREAK
+}
diff --git a/gdb/utils.c b/gdb/utils.c
index 0138c8e9fb6..9ca83246b24 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -723,36 +723,6 @@  myread (int desc, char *addr, int len)
   return orglen;
 }
 
-/* See utils.h.  */
-
-ULONGEST
-uinteger_pow (ULONGEST v1, LONGEST v2)
-{
-  if (v2 < 0)
-    {
-      if (v1 == 0)
-	error (_("Attempt to raise 0 to negative power."));
-      else
-	return 0;
-    }
-  else
-    {
-      /* The Russian Peasant's Algorithm.  */
-      ULONGEST v;
-
-      v = 1;
-      for (;;)
-	{
-	  if (v2 & 1L)
-	    v *= v1;
-	  v2 >>= 1;
-	  if (v2 == 0)
-	    return v;
-	  v1 *= v1;
-	}
-    }
-}
-
 
 
 /* An RAII class that sets up to handle input and then tears down
diff --git a/gdb/utils.h b/gdb/utils.h
index 6e240deccbf..a383036bcfe 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -303,13 +303,6 @@  extern pid_t wait_to_die_with_timeout (pid_t pid, int *status, int timeout);
 
 extern int myread (int, char *, int);
 
-/* Integer exponentiation: Return V1**V2, where both arguments
-   are integers.
-
-   Requires V1 != 0 if V2 < 0.
-   Returns 1 for 0 ** 0.  */
-extern ULONGEST uinteger_pow (ULONGEST v1, LONGEST v2);
-
 /* Resource limits used by getrlimit and setrlimit.  */
 
 enum resource_limit_kind
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 99597bef695..a8ae17e1730 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -34,13 +34,6 @@  static struct value *value_subscripted_rvalue (struct value *array,
 					       LONGEST index,
 					       LONGEST lowerbound);
 
-/* Define whether or not the C operator '/' truncates towards zero for
-   differently signed operands (truncation direction is undefined in C).  */
-
-#ifndef TRUNCATION_TOWARDS_ZERO
-#define TRUNCATION_TOWARDS_ZERO ((-5 / 2) == -2)
-#endif
-
 /* Given a pointer, return the size of its target.
    If the pointer type is void *, then return 1.
    If the target type is incomplete, then error out.
@@ -726,36 +719,6 @@  value_concat (struct value *arg1, struct value *arg2)
   return result;
 }
 
-/* Integer exponentiation: V1**V2, where both arguments are
-   integers.  Requires V1 != 0 if V2 < 0.  Returns 1 for 0 ** 0.  */
-
-static LONGEST
-integer_pow (LONGEST v1, LONGEST v2)
-{
-  if (v2 < 0)
-    {
-      if (v1 == 0)
-	error (_("Attempt to raise 0 to negative power."));
-      else
-	return 0;
-    }
-  else 
-    {
-      /* The Russian Peasant's Algorithm.  */
-      LONGEST v;
-      
-      v = 1;
-      for (;;)
-	{
-	  if (v2 & 1L) 
-	    v *= v1;
-	  v2 >>= 1;
-	  if (v2 == 0)
-	    return v;
-	  v1 *= v1;
-	}
-    }
-}
 
 /* Obtain argument values for binary operation, converting from
    other types if one of them is not floating point.  */
@@ -1099,33 +1062,39 @@  type_length_bits (type *type)
    both negative and too-large shift amounts, which are undefined, and
    would crash a GDB built with UBSan.  Depending on the current
    language, if the shift is not valid, this either warns and returns
-   false, or errors out.  Returns true if valid.  */
+   false, or errors out.  Returns true and sets NBITS if valid.  */
 
 static bool
 check_valid_shift_count (enum exp_opcode op, type *result_type,
-			 type *shift_count_type, ULONGEST shift_count)
+			 type *shift_count_type, const gdb_mpz &shift_count,
+			 unsigned long &nbits)
 {
-  if (!shift_count_type->is_unsigned () && (LONGEST) shift_count < 0)
+  if (!shift_count_type->is_unsigned ())
     {
-      auto error_or_warning = [] (const char *msg)
-      {
-	/* Shifts by a negative amount are always an error in Go.  Other
-	   languages are more permissive and their compilers just warn or
-	   have modes to disable the errors.  */
-	if (current_language->la_language == language_go)
-	  error (("%s"), msg);
-	else
-	  warning (("%s"), msg);
-      };
+      LONGEST count = shift_count.as_integer<LONGEST> ();
+      if (count < 0)
+	{
+	  auto error_or_warning = [] (const char *msg)
+	  {
+	    /* Shifts by a negative amount are always an error in Go.  Other
+	       languages are more permissive and their compilers just warn or
+	       have modes to disable the errors.  */
+	    if (current_language->la_language == language_go)
+	      error (("%s"), msg);
+	    else
+	      warning (("%s"), msg);
+	  };
 
-      if (op == BINOP_RSH)
-	error_or_warning (_("right shift count is negative"));
-      else
-	error_or_warning (_("left shift count is negative"));
-      return false;
+	  if (op == BINOP_RSH)
+	    error_or_warning (_("right shift count is negative"));
+	  else
+	    error_or_warning (_("left shift count is negative"));
+	  return false;
+	}
     }
 
-  if (shift_count >= type_length_bits (result_type))
+  nbits = shift_count.as_integer<unsigned long> ();
+  if (nbits >= type_length_bits (result_type))
     {
       /* In Go, shifting by large amounts is defined.  Be silent and
 	 still return false, as the caller's error path does the right
@@ -1249,299 +1218,135 @@  scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
       else
 	result_type = promotion_type (type1, type2);
 
-      if (result_type->is_unsigned ())
+      gdb_mpz v1 = value_as_mpz (arg1);
+      gdb_mpz v2 = value_as_mpz (arg2);
+      gdb_mpz v;
+
+      switch (op)
 	{
-	  LONGEST v2_signed = value_as_long (arg2);
-	  ULONGEST v1, v2, v = 0;
+	case BINOP_ADD:
+	  v = v1 + v2;
+	  break;
 
-	  v1 = (ULONGEST) value_as_long (arg1);
-	  v2 = (ULONGEST) v2_signed;
+	case BINOP_SUB:
+	  v = v1 - v2;
+	  break;
 
-	  switch (op)
-	    {
-	    case BINOP_ADD:
-	      v = v1 + v2;
-	      break;
-
-	    case BINOP_SUB:
-	      v = v1 - v2;
-	      break;
-
-	    case BINOP_MUL:
-	      v = v1 * v2;
-	      break;
-
-	    case BINOP_DIV:
-	    case BINOP_INTDIV:
-	      if (v2 != 0)
-		v = v1 / v2;
-	      else
-		error (_("Division by zero"));
-	      break;
-
-	    case BINOP_EXP:
-	      v = uinteger_pow (v1, v2_signed);
-	      break;
-
-	    case BINOP_REM:
-	      if (v2 != 0)
-		v = v1 % v2;
-	      else
-		error (_("Division by zero"));
-	      break;
-
-	    case BINOP_MOD:
-	      /* Knuth 1.2.4, integer only.  Note that unlike the C '%' op,
-		 v1 mod 0 has a defined value, v1.  */
-	      if (v2 == 0)
-		{
-		  v = v1;
-		}
-	      else
-		{
-		  v = v1 / v2;
-		  /* Note floor(v1/v2) == v1/v2 for unsigned.  */
-		  v = v1 - (v2 * v);
-		}
-	      break;
-
-	    case BINOP_LSH:
-	      if (!check_valid_shift_count (op, result_type, type2, v2))
-		v = 0;
-	      else
-		v = v1 << v2;
-	      break;
-
-	    case BINOP_RSH:
-	      if (!check_valid_shift_count (op, result_type, type2, v2))
-		v = 0;
-	      else
-		v = v1 >> v2;
-	      break;
-
-	    case BINOP_BITWISE_AND:
-	      v = v1 & v2;
-	      break;
-
-	    case BINOP_BITWISE_IOR:
-	      v = v1 | v2;
-	      break;
-
-	    case BINOP_BITWISE_XOR:
-	      v = v1 ^ v2;
-	      break;
-
-	    case BINOP_LOGICAL_AND:
-	      v = v1 && v2;
-	      break;
-
-	    case BINOP_LOGICAL_OR:
-	      v = v1 || v2;
-	      break;
-
-	    case BINOP_MIN:
-	      v = v1 < v2 ? v1 : v2;
-	      break;
-
-	    case BINOP_MAX:
-	      v = v1 > v2 ? v1 : v2;
-	      break;
-
-	    case BINOP_EQUAL:
-	      v = v1 == v2;
-	      break;
-
-	    case BINOP_NOTEQUAL:
-	      v = v1 != v2;
-	      break;
-
-	    case BINOP_LESS:
-	      v = v1 < v2;
-	      break;
-
-	    case BINOP_GTR:
-	      v = v1 > v2;
-	      break;
-
-	    case BINOP_LEQ:
-	      v = v1 <= v2;
-	      break;
-
-	    case BINOP_GEQ:
-	      v = v1 >= v2;
-	      break;
-
-	    default:
-	      error (_("Invalid binary operation on numbers."));
-	    }
+	case BINOP_MUL:
+	  v = v1 * v2;
+	  break;
 
-	  val = value::allocate (result_type);
-	  store_unsigned_integer (val->contents_raw ().data (),
-				  val->type ()->length (),
-				  type_byte_order (result_type),
-				  v);
-	}
-      else
-	{
-	  LONGEST v1, v2, v = 0;
+	case BINOP_DIV:
+	case BINOP_INTDIV:
+	  if (v2.sgn () != 0)
+	    v = v1 / v2;
+	  else
+	    error (_("Division by zero"));
+	  break;
 
-	  v1 = value_as_long (arg1);
-	  v2 = value_as_long (arg2);
+	case BINOP_EXP:
+	  v = v1.pow (v2.as_integer<unsigned long> ());
+	  break;
+
+	case BINOP_REM:
+	  if (v2.sgn () != 0)
+	    v = v1 % v2;
+	  else
+	    error (_("Division by zero"));
+	  break;
 
-	  switch (op)
+	case BINOP_MOD:
+	  /* Knuth 1.2.4, integer only.  Note that unlike the C '%' op,
+	     v1 mod 0 has a defined value, v1.  */
+	  if (v2.sgn () == 0)
+	    {
+	      v = v1;
+	    }
+	  else
 	    {
-	    case BINOP_ADD:
-	      v = v1 + v2;
-	      break;
-
-	    case BINOP_SUB:
-	      /* Avoid runtime error: signed integer overflow: \
-		 0 - -9223372036854775808 cannot be represented in type
-		 'long int'.  */
-	      v = (ULONGEST)v1 - (ULONGEST)v2;
-	      break;
-
-	    case BINOP_MUL:
-	      v = v1 * v2;
-	      break;
-
-	    case BINOP_DIV:
-	    case BINOP_INTDIV:
-	      if (v2 != 0)
-		v = v1 / v2;
-	      else
-		error (_("Division by zero"));
-	      break;
-
-	    case BINOP_EXP:
-	      v = integer_pow (v1, v2);
-	      break;
-
-	    case BINOP_REM:
-	      if (v2 != 0)
-		v = v1 % v2;
-	      else
-		error (_("Division by zero"));
-	      break;
-
-	    case BINOP_MOD:
-	      /* Knuth 1.2.4, integer only.  Note that unlike the C '%' op,
-		 X mod 0 has a defined value, X.  */
-	      if (v2 == 0)
-		{
-		  v = v1;
-		}
-	      else
-		{
-		  v = v1 / v2;
-		  /* Compute floor.  */
-		  if (TRUNCATION_TOWARDS_ZERO && (v < 0) && ((v1 % v2) != 0))
-		    {
-		      v--;
-		    }
-		  v = v1 - (v2 * v);
-		}
-	      break;
-
-	    case BINOP_LSH:
-	      if (!check_valid_shift_count (op, result_type, type2, v2))
-		v = 0;
-	      else
-		{
-		  /* Cast to unsigned to avoid undefined behavior on
-		     signed shift overflow (unless C++20 or later),
-		     which would crash GDB when built with UBSan.
-		     Note we don't warn on left signed shift overflow,
-		     because starting with C++20, that is actually
-		     defined behavior.  Also, note GDB assumes 2's
-		     complement throughout.  */
-		  v = (ULONGEST) v1 << v2;
-		}
-	      break;
-
-	    case BINOP_RSH:
-	      if (!check_valid_shift_count (op, result_type, type2, v2))
-		{
-		  /* Pretend the too-large shift was decomposed in a
-		     number of smaller shifts.  An arithmetic signed
-		     right shift of a negative number always yields -1
-		     with such semantics.  This is the right thing to
-		     do for Go, and we might as well do it for
-		     languages where it is undefined.  Also, pretend a
-		     shift by a negative number was a shift by the
-		     negative number cast to unsigned, which is the
-		     same as shifting by a too-large number.  */
-		  if (v1 < 0)
-		    v = -1;
-		  else
-		    v = 0;
-		}
-	      else
-		v = v1 >> v2;
-	      break;
-
-	    case BINOP_BITWISE_AND:
-	      v = v1 & v2;
-	      break;
-
-	    case BINOP_BITWISE_IOR:
-	      v = v1 | v2;
-	      break;
-
-	    case BINOP_BITWISE_XOR:
-	      v = v1 ^ v2;
-	      break;
-
-	    case BINOP_LOGICAL_AND:
-	      v = v1 && v2;
-	      break;
-
-	    case BINOP_LOGICAL_OR:
-	      v = v1 || v2;
-	      break;
-
-	    case BINOP_MIN:
-	      v = v1 < v2 ? v1 : v2;
-	      break;
-
-	    case BINOP_MAX:
-	      v = v1 > v2 ? v1 : v2;
-	      break;
-
-	    case BINOP_EQUAL:
-	      v = v1 == v2;
-	      break;
-
-	    case BINOP_NOTEQUAL:
-	      v = v1 != v2;
-	      break;
-
-	    case BINOP_LESS:
-	      v = v1 < v2;
-	      break;
-
-	    case BINOP_GTR:
-	      v = v1 > v2;
-	      break;
-
-	    case BINOP_LEQ:
-	      v = v1 <= v2;
-	      break;
-
-	    case BINOP_GEQ:
-	      v = v1 >= v2;
-	      break;
-
-	    default:
-	      error (_("Invalid binary operation on numbers."));
+	      v = v1 / v2;
+	      /* Note floor(v1/v2) == v1/v2 for unsigned.  */
+	      v = v1 - (v2 * v);
 	    }
+	  break;
 
-	  val = value::allocate (result_type);
-	  store_signed_integer (val->contents_raw ().data (),
-				val->type ()->length (),
-				type_byte_order (result_type),
-				v);
+	case BINOP_LSH:
+	  {
+	    unsigned long nbits;
+	    if (!check_valid_shift_count (op, result_type, type2, v2, nbits))
+	      v = 0;
+	    else
+	      v = v1 << nbits;
+	  }
+	  break;
+
+	case BINOP_RSH:
+	  {
+	    unsigned long nbits;
+	    if (!check_valid_shift_count (op, result_type, type2, v2, nbits))
+	      v = 0;
+	    else
+	      v = v1 >> nbits;
+	  }
+	  break;
+
+	case BINOP_BITWISE_AND:
+	  v = v1 & v2;
+	  break;
+
+	case BINOP_BITWISE_IOR:
+	  v = v1 | v2;
+	  break;
+
+	case BINOP_BITWISE_XOR:
+	  v = v1 ^ v2;
+	  break;
+
+	case BINOP_LOGICAL_AND:
+	  v = v1 && v2;
+	  break;
+
+	case BINOP_LOGICAL_OR:
+	  v = v1 || v2;
+	  break;
+
+	case BINOP_MIN:
+	  v = v1 < v2 ? v1 : v2;
+	  break;
+
+	case BINOP_MAX:
+	  v = v1 > v2 ? v1 : v2;
+	  break;
+
+	case BINOP_EQUAL:
+	  v = v1 == v2;
+	  break;
+
+	case BINOP_NOTEQUAL:
+	  v = v1 != v2;
+	  break;
+
+	case BINOP_LESS:
+	  v = v1 < v2;
+	  break;
+
+	case BINOP_GTR:
+	  v = v1 > v2;
+	  break;
+
+	case BINOP_LEQ:
+	  v = v1 <= v2;
+	  break;
+
+	case BINOP_GEQ:
+	  v = v1 >= v2;
+	  break;
+
+	default:
+	  error (_("Invalid binary operation on numbers."));
 	}
+
+      val = value_from_mpz (result_type, v);
     }
 
   return val;
@@ -1960,7 +1765,11 @@  value_complement (struct value *arg1)
   type = check_typedef (arg1->type ());
 
   if (is_integral_type (type))
-    val = value_from_longest (type, ~value_as_long (arg1));
+    {
+      gdb_mpz num = value_as_mpz (arg1);
+      num.complement ();
+      val = value_from_mpz (type, num);
+    }
   else if (type->code () == TYPE_CODE_ARRAY && type->is_vector ())
     {
       struct type *eltype = check_typedef (type->target_type ());
diff --git a/gdb/valops.c b/gdb/valops.c
index 2cef24fc4c5..f06969e92e6 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -569,7 +569,7 @@  value_cast (struct type *type, struct value *arg2)
 	   && (scalar || code2 == TYPE_CODE_PTR
 	       || code2 == TYPE_CODE_MEMBERPTR))
     {
-      LONGEST longest;
+      gdb_mpz longest;
 
       /* When we cast pointers to integers, we mustn't use
 	 gdbarch_pointer_to_address to find the address the pointer
@@ -578,12 +578,14 @@  value_cast (struct type *type, struct value *arg2)
 	 sees a cast as a simple reinterpretation of the pointer's
 	 bits.  */
       if (code2 == TYPE_CODE_PTR)
-	longest = extract_unsigned_integer
-	  (arg2->contents (), type_byte_order (type2));
+	longest = extract_unsigned_integer (arg2->contents (),
+					    type_byte_order (type2));
       else
-	longest = value_as_long (arg2);
-      return value_from_longest (to_type, convert_to_boolean ?
-				 (LONGEST) (longest ? 1 : 0) : longest);
+	longest = value_as_mpz (arg2);
+      if (convert_to_boolean)
+	longest = bool (longest);
+
+      return value_from_mpz (to_type, longest);
     }
   else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT  
 				      || code2 == TYPE_CODE_ENUM