Added support for duplicating a function.
authorJustin Seyster <jseyster@cs.sunysb.edu>
Wed, 11 Aug 2010 23:41:13 +0000 (19:41 -0400)
committerJustin Seyster <jseyster@cs.sunysb.edu>
Wed, 11 Aug 2010 23:41:13 +0000 (19:41 -0400)
src/Makefile.am
src/aop-duplicate.c [new file with mode: 0644]
src/aop-duplicate.h [new file with mode: 0644]
src/aop-weave.c
src/aop.h

index 7102bdfb266a7f76fae56ba4f3e08bce98f81d88..0c6c3d2ef9957d0fa175a168b30ab5835bef6a4c 100644 (file)
@@ -1,5 +1,7 @@
 lib_LTLIBRARIES = libinteraspect.la
-libinteraspect_la_SOURCES = aop-pc-assign.c aop-main.c aop-type.c aop-weave.c aop-pc-entry.c aop-pc-exit.c aop-pc-fun-call.c aop-pointcut.c
+libinteraspect_la_SOURCES = aop-pc-assign.c aop-main.c aop-type.c aop-weave.c \
+       aop-pc-entry.c aop-pc-exit.c aop-pc-fun-call.c aop-pointcut.c \
+       aop-duplicate.c
 libinteraspect_la_CFLAGS = -Wall -Werror -fvisibility=hidden -prefer-pic
 libinteraspect_la_LDFLAGS = -static -prefer-pic -version-info 1:0:0
 libinteraspect_la_CPPFLAGS = -DHAVE_CONFIG_H -DIN_GCC -I$(gcc_includes)
diff --git a/src/aop-duplicate.c b/src/aop-duplicate.c
new file mode 100644 (file)
index 0000000..b7d19bd
--- /dev/null
@@ -0,0 +1,308 @@
+/* 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/>.
+*/
+
+/* Whether we want them or not (we don't), Autoconf _insists_ on
+   defining these.  Since GCC's config.h (which we must include) also
+   defines them, we have to undef them here. */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+
+#include <locale.h>
+
+#include <config.h>
+#include <system.h>
+#include <coretypes.h>
+#include <tm.h>
+#include <basic-block.h>
+#include <gimple.h>
+#include <tree.h>
+#include <tree-flow.h>
+#include <tree-pass.h>
+#include <toplev.h>
+
+#include "aop.h"
+#include "aop-duplicate.h"
+
+typedef struct label_pair {
+  tree old;
+  tree new;
+} label_pair;
+
+#define INITIAL_PAIRS 10
+
+/* TODO: This needs to be garbage collected. */
+DEF_VEC_O(label_pair);
+DEF_VEC_ALLOC_O(label_pair, heap);
+
+#define FOR_EACH_BB_PAIR(pairs, index, pair)   \
+       for ((index)=0; VEC_iterate(bb_pair, pairs, index, pair); (index)++)
+
+#define FOR_EACH_LABEL_PAIR(pairs, index, pair)        \
+       for ((index)=0; VEC_iterate(label_pair, pairs, index, pair); (index)++)
+
+static basic_block 
+new_bb_for_old(VEC(bb_pair, gc) *bb_pairs, basic_block old)
+{
+  unsigned int pair_index;
+  bb_pair *pair;
+
+  FOR_EACH_BB_PAIR(bb_pairs, pair_index, pair)
+    {
+      if (pair->old == old)
+       return pair->new;
+    }
+
+  return NULL;
+}
+
+static tree 
+label_new_for_old(VEC(label_pair, heap) *label_pairs, tree old)
+{
+  unsigned int pair_index;
+  label_pair *pair;
+
+  FOR_EACH_LABEL_PAIR(label_pairs, pair_index, pair)
+    if (pair->old == old)
+      return pair->new;
+
+  return NULL;
+}
+
+static tree 
+fix_labels(tree *label, int *walk_subtrees, void *arg)
+{
+  struct walk_stmt_info *wi = (struct walk_stmt_info*) arg;
+       
+  if (TREE_CODE(*label) == LABEL_DECL)
+    {
+      tree new_label;
+      VEC(label_pair, heap) *label_pairs;
+       
+      label_pairs = (VEC(label_pair, heap) *)(wi->info);
+      new_label = label_new_for_old(label_pairs, *label);
+
+      aop_assert (new_label != NULL_TREE);
+      *label = new_label;
+    }
+
+  return NULL_TREE;
+}
+
+/* This function makes another copy of function body (blocks except entry
+   and exit along with their topology). This function also adds a distributor
+   block to direct to 2 bodies. The distributor block is like the following:
+
+   1  int tmpvar;
+   2  tmpvar = call(); 
+   3  if (tmpvar != 0) 
+   4    goto <NEW_BODY> 
+   5  else 
+   6    goto <OLD_BODY>;
+
+   If call is NULL, line 2 is replaced by tmpvar = 0. Then it is the user's 
+   duty to further populate the distributor block.
+ */
+void
+duplicate_function_body (VEC(bb_pair, gc) **bb_pairs_ptr,
+                        basic_block *distributor_bb_ptr,
+                        const char *tmpvar_name, gimple call)
+{
+  VEC(bb_pair, gc) *bb_pairs;
+  VEC(label_pair, heap) *label_pairs;
+
+  unsigned int pair_index;
+  bb_pair *pair_p;
+               
+  basic_block cur_bb;
+  basic_block last_bb;
+
+  bb_pairs = VEC_alloc (bb_pair, gc, INITIAL_PAIRS);
+  label_pairs = VEC_alloc (label_pair, heap, INITIAL_PAIRS);
+
+  /* Pass 1 - Collect a list of all the blocks that need duplication. */
+  FOR_EACH_BB (cur_bb)
+    {
+      if (can_duplicate_block_p (cur_bb))
+       {
+         bb_pair bbpair;
+         bbpair.old = cur_bb;
+         bbpair.new = NULL;
+         VEC_safe_push (bb_pair, gc, bb_pairs, &bbpair);
+       }
+      else
+       {
+         /* As of writing, only the entry and exit blocks cannot be
+            duplicated. */
+         aop_assert (cur_bb == EXIT_BLOCK_PTR || cur_bb == ENTRY_BLOCK_PTR);
+       }
+    }
+
+  /* Pass 2 - Duplicate the blocks in the list.  Refer to copy_bbs()
+     in cfglayout.c. */
+  initialize_original_copy_tables ();
+  last_bb = EXIT_BLOCK_PTR->prev_bb;
+
+  FOR_EACH_BB_PAIR (bb_pairs, pair_index, pair_p)
+    {
+      gimple old_first_stmt;
+
+      pair_p->new = duplicate_block (pair_p->old, NULL, last_bb);
+      last_bb = pair_p->new;
+
+      old_first_stmt = first_stmt (pair_p->old);
+      aop_assert (old_first_stmt != NULL);
+
+      /* duplicate_block() does not copy initial labels.  Calling
+        gimple_block_label() on the duplicated block will create the
+        label for us. */
+      if (gimple_code (old_first_stmt) == GIMPLE_LABEL)
+       {
+         label_pair lblpair;
+         lblpair.old = gimple_block_label (pair_p->old);
+         lblpair.new = gimple_block_label (pair_p->new);
+         DECL_SOURCE_LOCATION (lblpair.new) = DECL_SOURCE_LOCATION (lblpair.old);
+         VEC_safe_push (label_pair, heap, label_pairs, &lblpair);
+       }
+    }
+
+  /* Pass 3 - Fix all edges in the duplicate blocks and correct label
+     references. */
+  FOR_EACH_BB_PAIR (bb_pairs, pair_index, pair_p)
+    {
+      edge_iterator ei;
+      edge e;
+      gimple_stmt_iterator gsi;
+      struct walk_stmt_info wi;
+
+      /* Skip over initial label statements, which we want to leave
+        alone.  */
+      gsi = gsi_start_bb (pair_p->new);
+      while (!gsi_end_p (gsi) && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+       {
+         gsi_next (&gsi);
+       }
+      gcc_assert(!gsi_end_p(gsi));
+
+      for (; !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         memset (&wi, 0, sizeof (wi));
+         wi.info = (struct walk_stmt_info *)label_pairs;
+         walk_gimple_stmt (&gsi, NULL, fix_labels, &wi);
+       }
+
+      FOR_EACH_EDGE (e, ei, pair_p->new->succs)
+       {
+         basic_block succ_bb = e->dest;
+
+         if (can_duplicate_block_p(succ_bb))
+           {
+             basic_block new_bb;
+             edge redirected_edge;
+
+             new_bb = new_bb_for_old (bb_pairs, succ_bb);
+             aop_assert (new_bb != NULL);
+
+             redirected_edge = redirect_edge_and_branch (e, new_bb);
+             aop_assert (redirected_edge != NULL);
+           }
+       }
+    }
+
+  /* Pass 4 - Create the distributor block and bind it up */
+    {  
+      edge e;
+
+      basic_block old_first_bb;
+      basic_block new_first_bb;
+      basic_block distributor_bb;
+
+      tree old_label;
+      tree new_label;
+      tree tmpvar;
+
+      gimple cond;
+      gimple_stmt_iterator gsi;
+
+      e = single_succ_edge (ENTRY_BLOCK_PTR);
+
+      old_first_bb = e->dest;
+      new_first_bb = new_bb_for_old (bb_pairs, old_first_bb);
+      aop_assert (new_first_bb != NULL);
+
+      old_label = gimple_block_label (old_first_bb);
+      new_label = gimple_block_label (new_first_bb);
+
+      /* TODO: Can this line be removed? */
+      DECL_SOURCE_LOCATION(new_label) = DECL_SOURCE_LOCATION(old_label);
+      distributor_bb = split_edge(e);
+       
+      tmpvar = create_tmp_var(integer_type_node, tmpvar_name);
+      /*add_referenced_var(tmpvar);*/
+               
+      gsi = gsi_start_bb (distributor_bb);
+      if (call == NULL)
+       {
+         /* insert: tmpvar = 0; */     
+         gimple assign;
+         assign =
+           gimple_build_assign_with_ops (NOP_EXPR, tmpvar,
+                                         build_int_cst (integer_type_node, 0),
+                                         NULL_TREE);
+
+         /* TODO: Do we need this? */
+         if (gimple_in_ssa_p (cfun))
+           {
+             tmpvar = make_ssa_name (tmpvar, assign);
+             gimple_assign_set_lhs (assign, tmpvar);
+           }
+         gsi_insert_before (&gsi, assign, GSI_SAME_STMT);
+       }
+      else
+       {
+         /* insert: tmpvar = distributor_fn(...) */
+         /* TODO: No need to copy. */
+         gimple distributor_fn = gimple_copy(call);
+         aop_assert (gimple_code (distributor_fn) == GIMPLE_CALL);
+
+         /* TODO: Do we need this? */
+         if (gimple_in_ssa_p (cfun))
+           {
+             tmpvar = make_ssa_name (tmpvar, distributor_fn);
+           }
+         gimple_call_set_lhs (distributor_fn, tmpvar);
+         gsi_insert_before (&gsi, distributor_fn, GSI_SAME_STMT);
+       }       
+
+      /* insert: if (tmpvar) goto <new_label> else goto <old_label> */
+      cond = gimple_build_cond (NE_EXPR, tmpvar, 
+                               build_int_cst (integer_type_node, 0),
+                               new_label, old_label);
+      gsi_insert_before (&gsi, cond, GSI_SAME_STMT);
+
+      remove_edge(single_succ_edge(distributor_bb));
+      make_edge(distributor_bb, old_first_bb, EDGE_FALSE_VALUE);
+      make_edge(distributor_bb, new_first_bb, EDGE_TRUE_VALUE);
+
+      *distributor_bb_ptr = distributor_bb;
+    }
+
+  *bb_pairs_ptr = bb_pairs;
+
+  free_original_copy_tables();
+  VEC_free (label_pair, heap, label_pairs);
+  return;
+}
diff --git a/src/aop-duplicate.h b/src/aop-duplicate.h
new file mode 100644 (file)
index 0000000..878a549
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __AOP_DUPLICATE_H__
+#define __AOP_DUPLICATE_H__
+
+/* 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 is a private header.  Do not include it in source files for
+   client plug-ins. */
+
+typedef struct bb_pair {
+  basic_block old;
+  basic_block new;
+} bb_pair;
+
+DEF_VEC_O(bb_pair);
+DEF_VEC_ALLOC_O(bb_pair, gc);
+
+extern void duplicate_function_body (VEC(bb_pair, gc) **bb_pairs_ptr,
+                                    basic_block *distributor_bb_ptr,
+                                    const char *tmpvar_name, gimple call);
+
+#endif
index 492866699cc2541d1f50b45b386ea87a78918de7..2f84133f38e5b67d8ff2cc2879d9fee4de0b3ea6 100644 (file)
@@ -40,6 +40,7 @@
 #undef GENERATOR_FILE
 
 #include "aop.h"
+#include "aop-duplicate.h"
 #include "aop-dynval.h"
 #include "aop-pointcut.h"
 
@@ -262,3 +263,30 @@ aop_insert_advice (struct aop_joinpoint *jp, const char *func_name,
     pc->insert_after(jp, func_call);
 
 }
+
+void
+aop_duplicate (struct aop_joinpoint *jp, const char *func_name, ...)
+{
+  va_list argp;
+  gimple func_call;
+  struct aop_pointcut *pc;
+
+  VEC(bb_pair, gc) *bb_pairs;
+  basic_block distributor;
+
+  pc = jp->pc;
+
+  if (pc->kind != ATP_ENTRY)
+    fatal_error ("(InterAspect) Function duplication must be done on a"
+                " function entry join point.");
+  else if (jp->is_prepared)
+    fatal_error ("(InterAspect) Illegal to duplicate using a join point that"
+                " has already been used to insert advice.");
+
+  va_start (argp, func_name);
+  func_call = build_gcc_call (func_name, integer_type_node, AOP_INSERT_BEFORE,
+                             argp);
+  va_end (argp);
+
+  duplicate_function_body (&bb_pairs, &distributor, "ia_body_index", func_call);
+}
index 9e9b607dbf86e012fb43533ebaa72a1099dd141e..7a73d8045f75be17fb2b16b6c130c022f293d736 100644 (file)
--- a/src/aop.h
+++ b/src/aop.h
@@ -160,6 +160,7 @@ enum aop_insert_location {
 
 extern void aop_insert_advice (struct aop_joinpoint *jp, const char *name,
                               enum aop_insert_location location, ...);
+extern void aop_duplicate (struct aop_joinpoint *jp, const char *name, ...);
 
 extern const struct aop_type *aop_t_all_signed ();
 extern const struct aop_type *aop_t_all_unsigned ();