public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/arsenic/heads/polymorphic_call)] analyzer: detect and analyze vfunc calls
@ 2021-08-04 13:58 Ankur saini
  0 siblings, 0 replies; 2+ messages in thread
From: Ankur saini @ 2021-08-04 13:58 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:bda6341068a2319bb19a8ef68486bb379be66e48

commit bda6341068a2319bb19a8ef68486bb379be66e48
Author: Ankur Saini <arsenic@sourceware.org>
Date:   Thu Jul 29 17:20:10 2021 +0530

    analyzer: detect and analyze vfunc calls

Diff:
---
 gcc/analyzer/engine.cc        | 38 +++++++++++++-------
 gcc/analyzer/exploded-graph.h |  3 +-
 gcc/analyzer/program-state.cc |  5 +--
 gcc/analyzer/program-state.h  |  3 +-
 gcc/analyzer/region-model.cc  | 82 ++++++++++++++++++++++++++++++++++++++++---
 gcc/analyzer/region-model.h   |  3 +-
 6 files changed, 113 insertions(+), 21 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 60bd46abdff..dd3433a0f11 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -3037,12 +3037,14 @@ void
 exploded_graph::create_dynamic_call (const gcall *call,
                                      tree fn_decl,
                                      exploded_node *node,
-                                     program_state &next_state,
+                                     program_state next_state,
                                      program_point &next_point,
-                                     uncertainty_t *uncertainty)
+                                     uncertainty_t *uncertainty,
+                                     logger *logger)
 {
+  LOG_FUNC (logger);
+
   const program_point *this_point = &node->get_point ();
-  // assert for fn_decl ?
   function *fun = DECL_STRUCT_FUNCTION (fn_decl);
   if (fun)
     {
@@ -3057,12 +3059,16 @@ exploded_graph::create_dynamic_call (const gcall *call,
 
       new_point.push_to_call_stack (sn_exit,
   				    next_point.get_supernode());
-      next_state.push_call (*this, node, call, uncertainty);
+      next_state.push_call (*this, node, call, uncertainty, fn_decl);
 
-      // TODO: add some logging here regarding dynamic call
-      
       if (next_state.m_valid)
         {
+          if (logger)
+            logger->log ("Discovered call to %s [SN: %i -> SN: %i]",
+            		  function_name(fun),
+		          this_point->get_supernode ()->m_index,
+		          sn_entry->m_index);
+
           exploded_node *enode = get_or_create_node (new_point,
 					             next_state,
 					             node);
@@ -3312,7 +3318,10 @@ exploded_graph::process_node (exploded_node *node)
 	    program_state next_state (state);
 	    uncertainty_t uncertainty;
 
-	    /* Try to discover and analyse indirect function calls. */
+	    /* Try to discover and analyse indirect function calls.
+
+	       Some examples of such calls are virtual function calls
+	       and calls that happen via a function pointer.  */
             if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL
             	&& !(succ->get_any_callgraph_edge ()))
               {
@@ -3327,11 +3336,16 @@ exploded_graph::process_node (exploded_node *node)
                                                 point.get_stmt());
 
                 region_model *model = state.m_region_model;
-
-                /* Call is possibly happening via a function pointer.  */
-                if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt))
-                  create_dynamic_call (call, fn_decl, node, next_state,
-                                       next_point, &uncertainty);
+                if (tree fn_decl = model->get_fndecl_for_call (call,&ctxt))
+                  {
+                    create_dynamic_call (call,
+                                         fn_decl,
+                                         node,
+                                         next_state,
+                                         next_point,
+                                         &uncertainty,
+                                         logger);
+                  }
               }
 
 	    if (!node->on_edge (*this, succ, &next_point, &next_state,
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 291983ad7dc..c9edb9a4abf 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -818,7 +818,8 @@ public:
                             exploded_node *node,
                             program_state next_state,
                             program_point &next_point,
-                            uncertainty_t *uncertainty);
+                            uncertainty_t *uncertainty,
+                            logger *logger);
 
   exploded_node *get_or_create_node (const program_point &point,
 				     const program_state &state,
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index eb05994f0ef..b1c1d67bb8b 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1042,7 +1042,8 @@ void
 program_state::push_call (exploded_graph &eg,
       			  exploded_node *enode,
       			  const gcall *call_stmt,
-      			  uncertainty_t *uncertainty)
+      			  uncertainty_t *uncertainty,
+      			  tree fn_decl)
 {
   /* Update state.  */
   const program_point &point = enode->get_point ();
@@ -1053,7 +1054,7 @@ program_state::push_call (exploded_graph &eg,
           			  this,
           			  uncertainty,
           			  last_stmt);
-  m_region_model->update_for_gcall (call_stmt, &ctxt);
+  m_region_model->update_for_gcall(call_stmt, &ctxt, fn_decl);
 }
 
 /* Update this program_state to reflect a return from function
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 658dbb69075..f4bd4cbcf49 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -221,7 +221,8 @@ public:
   void push_call (exploded_graph &eg,
       		  exploded_node *enode,
       		  const gcall *call_stmt,
-      		  uncertainty_t *uncertainty);
+      		  uncertainty_t *uncertainty,
+      		  tree fn_decl = NULL);
 
   void returning_call (exploded_graph &eg,
       		       exploded_node *enode,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 1e86d1f3bf8..69fcdadc7ea 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "attribs.h"
 #include "tree-object-size.h"
+#include "ipa-utils.h"
 
 #if ENABLE_ANALYZER
 
@@ -3140,7 +3141,8 @@ region_model::maybe_update_for_edge (const superedge &edge,
 
 void
 region_model::update_for_gcall (const gcall *call_stmt,
-				region_model_context *ctxt)
+				region_model_context *ctxt,
+				tree fn_decl)
 {
   /* Build a vec of argument svalues, using the current top
      frame for resolving tree expressions.  */
@@ -3151,9 +3153,11 @@ region_model::update_for_gcall (const gcall *call_stmt,
       tree arg = gimple_call_arg (call_stmt, i);
       arg_svals.quick_push (get_rvalue (arg, ctxt));
     }
-
-  /* Get the function * from the call.  */
-  tree fn_decl = get_fndecl_for_call (call_stmt,ctxt);
+  
+  /* Get the fn_decl from the call if not provided as argument.  */
+  if (!fn_decl)
+    fn_decl = get_fndecl_for_call (call_stmt,ctxt);
+ 
   function *fun = DECL_STRUCT_FUNCTION (fn_decl);
   push_frame (fun, &arg_svals, ctxt);
 }
@@ -3654,6 +3658,76 @@ region_model::get_fndecl_for_call (const gcall *call,
 	}
     }
 
+  /* Call is possibly a polymorphic call.
+  
+     In such case, use devirtisation tools to find 
+     possible callees of this function call.  */
+  
+  function *fun = get_current_function ();
+  gcall *stmt  = const_cast<gcall *> (call);
+  cgraph_edge *e = cgraph_node::get (fun->decl)->get_edge (stmt);
+  if (e->indirect_info->polymorphic)
+  {
+    void *cache_token;
+    bool final;
+    vec <cgraph_node *> targets
+      = possible_polymorphic_call_targets (e, &final, &cache_token, true);
+    if (!targets.is_empty ())
+      {
+        tree most_propbable_taget = NULL_TREE;
+        if(targets.length () == 1)
+    	    return targets[0]->decl;
+    
+        /* From the current state, check which subclass the pointer that 
+           is being used to this polymorphic call points to, and use to
+           filter out correct function call.  */
+        tree t_val = gimple_call_arg (call, 0);
+        const svalue *sval = get_rvalue (t_val, ctxt);
+
+        const region *reg
+          = [&]()->const region *
+              {
+                switch (sval->get_kind ())
+                  {
+                    case SK_INITIAL:
+                      {
+                        const initial_svalue *initial_sval
+                          = sval->dyn_cast_initial_svalue ();
+                        return initial_sval->get_region ();
+              	      }
+              	      break;
+                    case SK_REGION:
+                      {
+                        const region_svalue *region_sval 
+                          = sval->dyn_cast_region_svalue ();
+                        return region_sval->get_pointee ();
+              	      }
+              	      break;
+
+                    default:
+                      return NULL;
+	          }
+              } ();
+
+        gcc_assert (reg);
+
+        tree known_possible_subclass_type;
+        known_possible_subclass_type = reg->get_type ();
+        if (reg->get_kind () == RK_FIELD)
+          {
+             const field_region* field_reg = reg->dyn_cast_field_region ();
+             known_possible_subclass_type = DECL_CONTEXT (field_reg->get_field ());
+          }
+
+        for (cgraph_node *x : targets)
+          {
+            if (DECL_CONTEXT (x->decl) == known_possible_subclass_type)
+              most_propbable_taget = x->decl;
+          }
+        return most_propbable_taget;
+      }
+   }
+
   return NULL_TREE;
 }
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index a15bc9e2f6d..f06788771dc 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -590,7 +590,8 @@ class region_model
 			      rejected_constraint **out);
 
   void update_for_gcall (const gcall *call_stmt,
-			 region_model_context *ctxt);
+			 region_model_context *ctxt,
+			 tree fn_decl = NULL);
   
   void update_for_return_gcall (const gcall *call_stmt,
 				region_model_context *ctxt);


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [gcc(refs/users/arsenic/heads/polymorphic_call)] analyzer: detect and analyze vfunc calls
@ 2021-08-04 15:50 Ankur saini
  0 siblings, 0 replies; 2+ messages in thread
From: Ankur saini @ 2021-08-04 15:50 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:7746e93ec6e7248eded75b62e63cc3a882fbb66f

commit 7746e93ec6e7248eded75b62e63cc3a882fbb66f
Author: Ankur Saini <arsenic@sourceware.org>
Date:   Thu Jul 29 17:20:10 2021 +0530

    analyzer: detect and analyze vfunc calls

Diff:
---
 gcc/analyzer/engine.cc        | 38 +++++++++++++-------
 gcc/analyzer/exploded-graph.h |  3 +-
 gcc/analyzer/program-state.cc |  5 +--
 gcc/analyzer/program-state.h  |  3 +-
 gcc/analyzer/region-model.cc  | 83 ++++++++++++++++++++++++++++++++++++++++---
 gcc/analyzer/region-model.h   |  3 +-
 6 files changed, 114 insertions(+), 21 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 60bd46abdff..dd3433a0f11 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -3037,12 +3037,14 @@ void
 exploded_graph::create_dynamic_call (const gcall *call,
                                      tree fn_decl,
                                      exploded_node *node,
-                                     program_state &next_state,
+                                     program_state next_state,
                                      program_point &next_point,
-                                     uncertainty_t *uncertainty)
+                                     uncertainty_t *uncertainty,
+                                     logger *logger)
 {
+  LOG_FUNC (logger);
+
   const program_point *this_point = &node->get_point ();
-  // assert for fn_decl ?
   function *fun = DECL_STRUCT_FUNCTION (fn_decl);
   if (fun)
     {
@@ -3057,12 +3059,16 @@ exploded_graph::create_dynamic_call (const gcall *call,
 
       new_point.push_to_call_stack (sn_exit,
   				    next_point.get_supernode());
-      next_state.push_call (*this, node, call, uncertainty);
+      next_state.push_call (*this, node, call, uncertainty, fn_decl);
 
-      // TODO: add some logging here regarding dynamic call
-      
       if (next_state.m_valid)
         {
+          if (logger)
+            logger->log ("Discovered call to %s [SN: %i -> SN: %i]",
+            		  function_name(fun),
+		          this_point->get_supernode ()->m_index,
+		          sn_entry->m_index);
+
           exploded_node *enode = get_or_create_node (new_point,
 					             next_state,
 					             node);
@@ -3312,7 +3318,10 @@ exploded_graph::process_node (exploded_node *node)
 	    program_state next_state (state);
 	    uncertainty_t uncertainty;
 
-	    /* Try to discover and analyse indirect function calls. */
+	    /* Try to discover and analyse indirect function calls.
+
+	       Some examples of such calls are virtual function calls
+	       and calls that happen via a function pointer.  */
             if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL
             	&& !(succ->get_any_callgraph_edge ()))
               {
@@ -3327,11 +3336,16 @@ exploded_graph::process_node (exploded_node *node)
                                                 point.get_stmt());
 
                 region_model *model = state.m_region_model;
-
-                /* Call is possibly happening via a function pointer.  */
-                if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt))
-                  create_dynamic_call (call, fn_decl, node, next_state,
-                                       next_point, &uncertainty);
+                if (tree fn_decl = model->get_fndecl_for_call (call,&ctxt))
+                  {
+                    create_dynamic_call (call,
+                                         fn_decl,
+                                         node,
+                                         next_state,
+                                         next_point,
+                                         &uncertainty,
+                                         logger);
+                  }
               }
 
 	    if (!node->on_edge (*this, succ, &next_point, &next_state,
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 291983ad7dc..c9edb9a4abf 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -818,7 +818,8 @@ public:
                             exploded_node *node,
                             program_state next_state,
                             program_point &next_point,
-                            uncertainty_t *uncertainty);
+                            uncertainty_t *uncertainty,
+                            logger *logger);
 
   exploded_node *get_or_create_node (const program_point &point,
 				     const program_state &state,
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index eb05994f0ef..b1c1d67bb8b 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1042,7 +1042,8 @@ void
 program_state::push_call (exploded_graph &eg,
       			  exploded_node *enode,
       			  const gcall *call_stmt,
-      			  uncertainty_t *uncertainty)
+      			  uncertainty_t *uncertainty,
+      			  tree fn_decl)
 {
   /* Update state.  */
   const program_point &point = enode->get_point ();
@@ -1053,7 +1054,7 @@ program_state::push_call (exploded_graph &eg,
           			  this,
           			  uncertainty,
           			  last_stmt);
-  m_region_model->update_for_gcall (call_stmt, &ctxt);
+  m_region_model->update_for_gcall(call_stmt, &ctxt, fn_decl);
 }
 
 /* Update this program_state to reflect a return from function
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 658dbb69075..f4bd4cbcf49 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -221,7 +221,8 @@ public:
   void push_call (exploded_graph &eg,
       		  exploded_node *enode,
       		  const gcall *call_stmt,
-      		  uncertainty_t *uncertainty);
+      		  uncertainty_t *uncertainty,
+      		  tree fn_decl = NULL);
 
   void returning_call (exploded_graph &eg,
       		       exploded_node *enode,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 1e86d1f3bf8..300ed3f17d0 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "attribs.h"
 #include "tree-object-size.h"
+#include "ipa-utils.h"
 
 #if ENABLE_ANALYZER
 
@@ -3140,7 +3141,8 @@ region_model::maybe_update_for_edge (const superedge &edge,
 
 void
 region_model::update_for_gcall (const gcall *call_stmt,
-				region_model_context *ctxt)
+				region_model_context *ctxt,
+				tree fn_decl)
 {
   /* Build a vec of argument svalues, using the current top
      frame for resolving tree expressions.  */
@@ -3151,9 +3153,11 @@ region_model::update_for_gcall (const gcall *call_stmt,
       tree arg = gimple_call_arg (call_stmt, i);
       arg_svals.quick_push (get_rvalue (arg, ctxt));
     }
-
-  /* Get the function * from the call.  */
-  tree fn_decl = get_fndecl_for_call (call_stmt,ctxt);
+  
+  /* Get the fn_decl from the call if not provided as argument.  */
+  if (!fn_decl)
+    fn_decl = get_fndecl_for_call (call_stmt,ctxt);
+ 
   function *fun = DECL_STRUCT_FUNCTION (fn_decl);
   push_frame (fun, &arg_svals, ctxt);
 }
@@ -3654,6 +3658,77 @@ region_model::get_fndecl_for_call (const gcall *call,
 	}
     }
 
+  /* Call is possibly a polymorphic call.
+  
+     In such case, use devirtisation tools to find 
+     possible callees of this function call.  */
+  
+  function *fun = get_current_function ();
+  gcall *stmt  = const_cast<gcall *> (call);
+  cgraph_edge *e = cgraph_node::get (fun->decl)->get_edge (stmt);
+  if (e->indirect_info->polymorphic)
+  {
+    void *cache_token;
+    bool final;
+    vec <cgraph_node *> targets
+      = possible_polymorphic_call_targets (e, &final, &cache_token, true);
+    if (!targets.is_empty ())
+      {
+        tree most_propbable_taget = NULL_TREE;
+        if(targets.length () == 1)
+    	    return targets[0]->decl;
+    
+        /* From the current state, check which subclass the pointer that 
+           is being used to this polymorphic call points to, and use to
+           filter out correct function call.  */
+        tree t_val = gimple_call_arg (call, 0);
+        const svalue *sval = get_rvalue (t_val, ctxt);
+
+        const region *reg
+          = [&]()->const region *
+              {
+                switch (sval->get_kind ())
+                  {
+                    case SK_INITIAL:
+                      {
+                        const initial_svalue *initial_sval
+                          = sval->dyn_cast_initial_svalue ();
+                        return initial_sval->get_region ();
+                      }
+                      break;
+                    case SK_REGION:
+                      {
+                        const region_svalue *region_sval 
+                          = sval->dyn_cast_region_svalue ();
+                        return region_sval->get_pointee ();
+                      }
+                      break;
+
+                    default:
+                      return NULL;
+                  }
+              } ();
+
+        gcc_assert (reg);
+
+        tree known_possible_subclass_type;
+        known_possible_subclass_type = reg->get_type ();
+        if (reg->get_kind () == RK_FIELD)
+          {
+             const field_region* field_reg = reg->dyn_cast_field_region ();
+             known_possible_subclass_type 
+               = DECL_CONTEXT (field_reg->get_field ());
+          }
+
+        for (cgraph_node *x : targets)
+          {
+            if (DECL_CONTEXT (x->decl) == known_possible_subclass_type)
+              most_propbable_taget = x->decl;
+          }
+        return most_propbable_taget;
+      }
+   }
+
   return NULL_TREE;
 }
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index a15bc9e2f6d..f06788771dc 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -590,7 +590,8 @@ class region_model
 			      rejected_constraint **out);
 
   void update_for_gcall (const gcall *call_stmt,
-			 region_model_context *ctxt);
+			 region_model_context *ctxt,
+			 tree fn_decl = NULL);
   
   void update_for_return_gcall (const gcall *call_stmt,
 				region_model_context *ctxt);


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-08-04 15:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-04 13:58 [gcc(refs/users/arsenic/heads/polymorphic_call)] analyzer: detect and analyze vfunc calls Ankur saini
2021-08-04 15:50 Ankur saini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).