diff mbox

[2/2] Enable having chained function calls in expressions

Message ID CAGyQ6gx1PvJriP=1ZqHcZRfLNzAU+Mqq1woVD8h34k6wU1jyBw@mail.gmail.com
State New
Headers show

Commit Message

Siva Chandra Reddy Sept. 26, 2014, 1:30 p.m. UTC
Enable having chained function calls in expressions.

gdb/ChangeLog:

2014-09-26  Siva Chandra Reddy  <sivachandra@google.com>

        * eval.c (evaluate_expression): Cleanup stack mirrors that might
        have been created.
        (add_value_to_expression_stack, skip_current_expression_stack): New
        functions.
        * infcall.c (call_function_by_hand): Setup stack mirrors for return
        values.
        * value.h (add_value_to_expression_stack)
        (skip_current_expression_stack): Declare.

gdb/testsuite/ChangeLog:

2014-09-26  Siva Chandra Reddy  <sivachandra@google.com>

        * gdb.cp/chained-calls.cc: New file.
        * gdb.cp/chained-calls.exp: New file.
diff mbox

Patch

diff --git a/gdb/eval.c b/gdb/eval.c
index c379209..5095666 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -39,6 +39,7 @@ 
 #include "valprint.h"
 #include "gdb_obstack.h"
 #include "objfiles.h"
+#include "common/vec.h"
 #include <ctype.h>
 
 /* This is defined in valops.c */
@@ -60,6 +61,12 @@  static LONGEST init_array_element (struct value *, struct value *,
 				   struct expression *, int *, enum noside,
 				   LONGEST, LONGEST);
 
+typedef struct value *value_ptr;
+DEF_VEC_P (value_ptr);
+typedef VEC (value_ptr) value_vec;
+
+static value_vec *expr_stack_mirror_vec = NULL;
+
 struct value *
 evaluate_subexp (struct type *expect_type, struct expression *exp,
 		 int *pos, enum noside noside)
@@ -138,8 +145,60 @@  struct value *
 evaluate_expression (struct expression *exp)
 {
   int pc = 0;
+  struct value *res;
+  struct cleanup *cleanups;
+
+  expr_stack_mirror_vec = VEC_alloc (value_ptr, 5);
+  cleanups = make_cleanup (VEC_cleanup (value_ptr), &expr_stack_mirror_vec);
+
+  res = evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL);
+  do_cleanups (cleanups);
+
+  return res;
+
+}
+
+/* Add value V to the expression stack.  */
+
+void
+add_value_to_expression_stack (struct value *v)
+{
+  if (expr_stack_mirror_vec == NULL)
+    return;
+
+  VEC_safe_push (value_ptr, expr_stack_mirror_vec, v);
+}
+
+/* Return an address after skipping over the current values on the expression
+   stack.  SP is the current stack frame pointer.  Non-zero DOWNWARD indicates
+   that the stack grows downwards/backwards.  */
 
-  return evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL);
+CORE_ADDR
+skip_current_expression_stack (CORE_ADDR sp, int downward)
+{
+  CORE_ADDR addr = sp;
+
+  if (!VEC_empty (value_ptr, expr_stack_mirror_vec))
+    {
+      struct value *v = VEC_last (value_ptr, expr_stack_mirror_vec);
+      CORE_ADDR val_addr = value_address (v);
+
+      if (downward)
+	{
+	  gdb_assert (sp >= val_addr);
+	  addr = val_addr;
+	}
+      else
+	{
+	  struct type *type;
+
+	  gdb_assert (sp <= val_addr);
+	  type = value_type (v);
+	  addr = val_addr + TYPE_LENGTH (type);
+	}
+    }
+
+  return addr;
 }
 
 /* Evaluate an expression, avoiding all memory references
diff --git a/gdb/infcall.c b/gdb/infcall.c
index e60d1d4..baf12ad 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -533,6 +533,13 @@  call_function_by_hand (struct value *function, int nargs, struct value **args)
   {
     CORE_ADDR old_sp = get_frame_sp (frame);
 
+    /* Skip over the stack mirrors that might have been generated during the
+       evaluation of the current expression.  */
+    if (gdbarch_inner_than (gdbarch, 1, 2))
+      old_sp = skip_current_expression_stack (old_sp, 1);
+    else
+      old_sp = skip_current_expression_stack (old_sp, 0);
+
     if (gdbarch_frame_align_p (gdbarch))
       {
 	sp = gdbarch_frame_align (gdbarch, old_sp);
@@ -719,10 +726,16 @@  call_function_by_hand (struct value *function, int nargs, struct value **args)
   }
 
   /* Reserve space for the return structure to be written on the
-     stack, if necessary.  Make certain that the value is correctly
-     aligned.  */
+     stack unless the return type is void.  Make certain that the value is
+     correctly aligned.
+
+     We reserve space on the stack even if the language ABI and the target
+     ABI do not require that the return value be passed as an hidden first
+     argument.  This is because we want to store the return value as an
+     on-stack mirror of the actual value returned by this function.  This
+     enables us to have chained function calls in expressions.  */
 
-  if (struct_return || hidden_first_param_p)
+  if (TYPE_CODE (values_type) != TYPE_CODE_VOID)
     {
       if (gdbarch_inner_than (gdbarch, 1, 2))
 	{
@@ -1060,13 +1073,18 @@  When the function is done executing, GDB will silently stop."),
        At this stage, leave the RETBUF alone.  */
     restore_infcall_control_state (inf_status);
 
-    /* Figure out the value returned by the function.  */
-    retval = allocate_value (values_type);
-
     if (hidden_first_param_p)
-      read_value_memory (retval, 0, 1, struct_addr,
-			 value_contents_raw (retval),
-			 TYPE_LENGTH (values_type));
+      {
+	struct value *mirror;
+
+	mirror = value_from_contents_and_address (values_type, NULL,
+						  struct_addr);
+	add_value_to_expression_stack (mirror);
+        retval = allocate_value_mirrored_on_stack (mirror);
+	read_value_memory (retval, 0, 1, struct_addr,
+			   value_contents_raw (retval),
+			   TYPE_LENGTH (values_type));
+      }
     else if (TYPE_CODE (target_values_type) != TYPE_CODE_VOID)
       {
 	/* If the function returns void, don't bother fetching the
@@ -1077,16 +1095,28 @@  When the function is done executing, GDB will silently stop."),
 	  case RETURN_VALUE_REGISTER_CONVENTION:
 	  case RETURN_VALUE_ABI_RETURNS_ADDRESS:
 	  case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
+	    retval = allocate_value (values_type);
 	    gdbarch_return_value (gdbarch, function, values_type,
 				  retbuf, value_contents_raw (retval), NULL);
+	    setup_stack_mirror (retval, struct_addr);
 	    break;
 	  case RETURN_VALUE_STRUCT_CONVENTION:
-	    read_value_memory (retval, 0, 1, struct_addr,
-			       value_contents_raw (retval),
-			       TYPE_LENGTH (values_type));
+	    {
+	      struct value *mirror;
+
+	      mirror = value_from_contents_and_address (values_type, NULL,
+							struct_addr);
+	      retval = allocate_value_mirrored_on_stack (mirror);
+	      add_value_to_expression_stack (mirror);
+	      read_value_memory (retval, 0, 1, struct_addr,
+				 value_contents_raw (retval),
+				 TYPE_LENGTH (values_type));
+	    }
 	    break;
 	  }
       }
+    else
+      retval = allocate_value (values_type);
 
     do_cleanups (retbuf_cleanup);
 
diff --git a/gdb/testsuite/gdb.cp/chained-calls.cc b/gdb/testsuite/gdb.cp/chained-calls.cc
new file mode 100644
index 0000000..1a30631
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/chained-calls.cc
@@ -0,0 +1,113 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014 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/>.  */
+
+class S 
+{
+public:
+  S () { }
+  S (S &obj);
+
+  S operator+ (const S &s);
+
+  int a;
+};
+
+S::S (S &obj)
+{
+  a = obj.a;
+}
+
+S
+S::operator+ (const S &s)
+{
+  S res;
+
+  res.a = a + s.a;
+
+  return res;
+}
+
+S
+f (int i)
+{
+  S s;
+
+  s.a = i;
+
+  return s;
+}
+
+int
+g (const S &s)
+{
+  return s.a;
+}
+
+class A
+{
+public:
+  int a;
+};
+
+A
+p ()
+{
+  A a;
+  a.a = 12345678;
+  return a;
+}
+
+A
+q (const A &a)
+{
+  return a;
+}
+
+class B
+{
+public:
+  int b[1024];
+};
+
+B
+makeb ()
+{
+  B b;
+  int i;
+
+  for (i = 0; i < 1024; i++)
+    b.b[i] = i;
+
+  return b;
+}
+
+int
+getb (const B &b, int i)
+{
+  return b.b[i];
+}
+
+int
+main ()
+{ 
+  int i = g(f(0));
+  A a = q(p());
+
+  B b = makeb ();
+
+  return i + getb(b, 0);  /* Break here  */
+}
diff --git a/gdb/testsuite/gdb.cp/chained-calls.exp b/gdb/testsuite/gdb.cp/chained-calls.exp
new file mode 100644
index 0000000..ac20c85
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/chained-calls.exp
@@ -0,0 +1,37 @@ 
+# Copyright 2014 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/>.
+
+# This file is part of the gdb testsuite
+
+if {[skip_cplus_tests]} { continue }
+
+standard_testfile .cc
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+    return -1
+}
+
+if {![runto_main]} {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "Break here"
+
+gdb_test "p g(f(12345))" ".*12345.*" "g(f())"
+gdb_test "p q(p())" ".*12345678.*" "q(p())"
+gdb_test "p g(f(6700) + f(89))" ".*6789.*" "g(f() + f())"
+gdb_test "p g(f(g(f(300) + f(40))) + f(5))" ".*345.*" "g(f(g(f() + f())) + f())"
+gdb_test "p getb(makeb(), 789)" ".*789" "getb(makeb(), ...)"
diff --git a/gdb/value.h b/gdb/value.h
index 555a980..58fe7d5 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -823,6 +823,10 @@  extern struct value *evaluate_subexp (struct type *expect_type,
 extern struct value *evaluate_subexpression_type (struct expression *exp,
 						  int subexp);
 
+extern void add_value_to_expression_stack (struct value *v);
+
+extern CORE_ADDR skip_current_expression_stack (CORE_ADDR sp, int downward);
+
 extern void fetch_subexp_value (struct expression *exp, int *pc,
 				struct value **valp, struct value **resultp,
 				struct value **val_chain,