Added support for aop_capture_lhs_addr() on assignment pointcuts.
authorJustin Seyster <jseyster@cs.sunysb.edu>
Thu, 1 Apr 2010 20:27:35 +0000 (16:27 -0400)
committerJustin Seyster <jseyster@cs.sunysb.edu>
Thu, 1 Apr 2010 20:27:35 +0000 (16:27 -0400)
src/aop-pc-assign.c
src/aop-pointcut.h
src/aop.h

index 0e54209872543361320a61aac8b0b97295929a61..37e5b9637193e12de129304b1912bcf2873b1f7d 100644 (file)
 #include <tree.h>
 #include <ggc.h>
 #include <basic-block.h>
+#include <tree-flow.h>
 #include <gimple.h>
 
 #include "aop.h"
+#include "aop-dynval.h"
 #include "aop-pointcut.h"
 #include "aop-type.h"
 
+/**
+ * Get the name of the variable being assigned to in an assignment
+ * joinpoint.
+ * \param jp An assignment joinpoint.  Assignment joinpoints are
+ * obtained by joining on an aop_match_assignment_by_type() pointcut.
+ * \return For a direct assignment to a variable, the name of that
+ * variable, otherwise NULL.
+ */
 const char *
 aop_capture_lhs_name (struct aop_joinpoint *jp)
 {
@@ -65,13 +75,119 @@ aop_capture_lhs_name (struct aop_joinpoint *jp)
     }
 }
 
+/* Given an expression from the lhs of an assignment, return true
+   unless it is a temporary variable created by the compiler.
+
+   Generally, users will not be interested in instrumenting those temp
+   variables that return false here.  */
+static bool
+lhs_is_real (tree lhs)
+{
+  if (TREE_CODE (lhs) == SSA_NAME)
+    lhs = SSA_NAME_VAR (lhs);
+
+  /* DECL_VAR nodes with DECL_ARTIFICIAL set represent temporary
+     variables. */
+  if (TREE_CODE (lhs) == VAR_DECL)
+    return !DECL_ARTIFICIAL (lhs);
+  else
+    return true;
+}
+
+/* This gets called when weaving an advice function that evaluates an
+   aop_capture_lhs_addr() dynval.  It does the actual work of
+   inserting statements to get a reference to &lhs and then returns
+   that reference. */
+static tree
+op_get_lhs_addr (struct aop_dynval *dv)
+{
+  gimple stmt;
+  tree lhs;
+  tree lhs_pointer;
+
+  struct aop_joinpoint *jp = dv->jp;
+
+  aop_assert (dv->kind == ADV_LHS_ADDR);
+  aop_assert (jp->pc->kind == ATP_ASSIGN);
+
+  stmt = jp->stmt;
+  aop_assert (gimple_has_lhs (stmt));
+  lhs = gimple_get_lhs (stmt);
+
+  aop_assert (lhs_is_real (lhs));
+
+  if (TREE_CODE (lhs) == SSA_NAME)
+    lhs = SSA_NAME_VAR (lhs);
+
+  /* GCC only marks a variable as addressable if the program actually
+     takes its address (allowing certain optimizations on those
+     variables that are not addressable).  In order to take the
+     address ourselves, we must make sure that the variable is marked
+     addressable. */
+  mark_addressable (lhs);
+  lhs_pointer = build1 (ADDR_EXPR, build_pointer_type (void_type_node),
+                       stabilize_reference (lhs));
+
+  /* If mark_addressable() fails to make a variable addressable, it is
+     impossible to address that variable. */
+  aop_assert (is_gimple_address (lhs_pointer));
+
+  return lhs_pointer;
+}
+
+/** 
+ * Get a dynval representing the address of the variable being
+ * assigned to at an assignment joinpoint.
+ * Note that this capture function will return NULL if the joinpoint
+ * is an assignment to a temporary variable: temporary variables are
+ * never addressable.  You only need to worry about non-addressable
+ * temporary variables in a pointcut if you filter it with
+ * aop_filter_include_temps().
+ * \param jp An assignment joinpoint.  Assignment joinpoints are
+ * obtained by joining on an aop_match_assignment_by_type() pointcut.
+ * \return A dynval with (void *) type or NULL if the assignment
+ * target is not addressable.
+ */
+struct aop_dynval *
+aop_capture_lhs_addr (struct aop_joinpoint *jp)
+{
+  tree lhs;
+  struct aop_dynval *dv;
+
+  aop_assert (jp->pc->kind == ATP_ASSIGN);
+
+  /* Is this a temporary? */
+  aop_assert (gimple_has_lhs (jp->stmt));
+  lhs = gimple_get_lhs (jp->stmt);
+  if (!lhs_is_real (lhs))
+    {
+      /* We should only see temp vars if this is a pointcut that
+        includes them. */
+      aop_assert (jp->pc->pc_assign.include_temp_vars);
+      return NULL;
+    }
+
+  dv = ggc_alloc (sizeof (struct aop_dynval));
+  dv->kind = ADV_LHS_ADDR;
+  dv->type = aop_t_all_pointer ();
+  dv->jp = jp;
+  dv->get_dynval = op_get_lhs_addr;
+
+  return dv;
+}
+
 static bool
 stmt_matches_pointcut (struct aop_pointcut *pc, gimple stmt)
 {
   if (gimple_has_lhs (stmt))
     {
+      tree lhs = gimple_get_lhs(stmt);
       tree type = TREE_TYPE (gimple_get_lhs (stmt));
-      return does_type_match (type, pc->pc_assign.type);
+
+      /* Temp variables with matching type only match the pointcut if
+        include_temp_vars is true. */
+      return does_type_match (type, pc->pc_assign.type)
+       && (pc->pc_assign.include_temp_vars || lhs_is_real (lhs));
     }
   else
     {
@@ -100,6 +216,7 @@ op_join_on_assign (struct aop_pointcut *pc, join_callback cb,
              struct aop_joinpoint jp;
              jp.pc = pc;
              jp.gsi = &gsi;
+             jp.stmt = stmt;
 
              cb (&jp, callback_param);
            }
@@ -107,17 +224,55 @@ op_join_on_assign (struct aop_pointcut *pc, join_callback cb,
     }
 }
 
+/**
+ * Include compiler-created temporary variables in an assignment
+ * pointcut.
+ *
+ * By default, assignment pointcuts do not include assignments to
+ * temporary variables.  These assignments will usually only be useful
+ * to users trying to debug the compiler with InterAsepct.
+ * \param pc The pointcut to modify with the filter.
+ */
+void
+aop_filter_include_temps (struct aop_pointcut *pc)
+{
+  aop_assert (pc->kind == ATP_ASSIGN);
+  pc->pc_assign.include_temp_vars = true;
+}
+
+/**
+ * Exclude compiler-created temporary variables from an assignment
+ * pointcut.
+ *
+ * This function is provided to undo the effect of
+ * aop_filter_include_temps(); by default, you do not need to call it.
+ * \param pc The pointcut to modify with the filter.
+ */
+void
+aop_filter_exclude_temps (struct aop_pointcut *pc)
+{
+  aop_assert (pc->kind == ATP_ASSIGN);
+  pc->pc_assign.include_temp_vars = false;
+}
+
+/**
+ * Return a pointcut that matches all assignments to a variable with
+ * the given type.
+ * \param type The type to filter assignments by.
+ * \return The resulting pointcut.
+ */
 struct aop_pointcut *
 aop_match_assignment_by_type (const struct aop_type *type)
 {
   struct aop_pointcut *pc;
 
-  pc = ggc_alloc (sizeof (struct aop_pointcut *));
+  pc = ggc_alloc (sizeof (struct aop_pointcut));
   pc->kind = ATP_ASSIGN;
   pc->join_on = op_join_on_assign;
   pc->insert_before = op_default_insert_before;
 
   pc->pc_assign.type = type;
+  pc->pc_assign.include_temp_vars = false;
 
   return pc;
 }
index cfba5fc77369dab6ec923696bdb45a12b1ec293f..85420b331e8e633e9c67f6716cfdba320b20007c 100644 (file)
@@ -18,6 +18,8 @@
 /* This is a private header.  Do not include it in source file for
    client plug-ins. */
 
+#include "aop-callback.h"
+
 struct aop_type;
 
 enum aop_pckind {
@@ -26,11 +28,16 @@ enum aop_pckind {
   ATP_EXIT,
 };
 
+/* Information specific to assignment pointcuts. */
 struct aop_pc_assign {
   const struct aop_type *type;
+
+  /* If true, include assignments to temporary variables in the
+     pointcut.  This is false by default. */
+  bool include_temp_vars;
 };
 
-/* An AOP pointcut represents the a set of joinponts: locations in the
+/* An AOP pointcut represents a set of joinponts: locations in the
    source code that are available for inserting instrumentation.
 
    In practice, we do not directly store the set of joinpoints.
@@ -40,7 +47,7 @@ struct aop_pointcut {
   enum aop_pckind kind;
 
   void (*join_on) (struct aop_pointcut *, join_callback, void *);
-  void (*insert_before) (struct aop_joinpoint *, gimple);
+  insert_callback insert_before;
 
   union {
     struct aop_pc_assign pc_assign;
@@ -58,6 +65,9 @@ struct aop_joinpoint {
   /* A GCC iterator for where weave functions can insert their
      instrumentation.*/
   gimple_stmt_iterator *gsi;
+
+  /* The GIMPLE statement being instrumented (where relevant). */
+  gimple stmt;
 };
 
 void op_default_insert_before (struct aop_joinpoint *jp, gimple stmt);
index 6f53d8afb1129198e7b61cb160b8bb6655cfd47d..36756156fe8ef5e358ce7e530078574b4f0fd721 100644 (file)
--- a/src/aop.h
+++ b/src/aop.h
@@ -47,6 +47,7 @@
 #define aop_assert(EXPR) ((void)(0 && (EXPR)))
 #endif
 
+struct aop_dynval;
 struct aop_joinpoint;
 struct aop_pointcut;
 struct aop_type;
@@ -57,6 +58,9 @@ typedef void (*join_callback) (struct aop_joinpoint *, void *callback_param);
 extern struct aop_pointcut *aop_match_assignment_by_type (
   const struct aop_type *type);
 
+extern void aop_filter_include_temps (struct aop_pointcut *pc);
+extern void aop_filter_exclude_temps (struct aop_pointcut *pc);
+
 extern const char *aop_capture_lhs_name (struct aop_joinpoint *jp);
 
 extern void aop_insert_advice (struct aop_joinpoint *jp, const char *name, ...);
@@ -87,6 +91,7 @@ enum aop_argkind {
 #define AOP_DYNVAL(VAL) ATA_DYNVAL, VAL
 
 const char *aop_capture_function_name (struct aop_joinpoint *jp);
+struct aop_dynval *aop_capture_lhs_addr (struct aop_joinpoint *jp);
 struct aop_pointcut *aop_match_function_entry ();
 struct aop_pointcut *aop_match_function_exit ();