From 274580edc6508b01c57557a40193a00edc3e487b Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 13 Apr 2017 13:59:41 -0400
Subject: [PATCH 04/10] FIXME: WIP on eliminating alloca

---
 gcc/heap-optimizations.c                 | 122 +++++++++++++++++++++-
 gcc/passes.def                           |   5 +-
 gcc/testsuite/gcc.dg/heap-optimization.c | 169 +++++++++++++++++++++++++++++++
 3 files changed, 292 insertions(+), 4 deletions(-)

diff --git a/gcc/heap-optimizations.c b/gcc/heap-optimizations.c
index b7fa46c..6c0c505 100644
--- a/gcc/heap-optimizations.c
+++ b/gcc/heap-optimizations.c
@@ -195,6 +195,7 @@ class pass_heap_optimizations : public gimple_opt_pass
  private:
   void handle_malloc (gcall *call);
   void handle_free (function_state &state, gcall *call);
+  void maybe_optimize_alloca (gcall *call);
 }; // class pass_heap_optimizations
 
 void
@@ -225,6 +226,7 @@ allocation_group::combine_calls ()
      - this can't work in the presence of realloc: detect realloc
      - usage of calloc.  */
 
+#if 0
   /* Sum the sizes of the allocations.  */
   // FIXME: this assumes that all sizes are available before any ptrs are used.
   tree sum = size_zero_node;
@@ -237,8 +239,12 @@ allocation_group::combine_calls ()
 				    PLUS_EXPR, size_type_node, sum, cast);
       sum = next_sum;
     }
+#endif
 
-#if 1
+  // gcall *gimple_build_call_internal (enum internal_fn, unsigned, ...);
+
+
+#if 0
   for (int j = 0; j < 5; j++)
     {
       gimple *stmt = gimple_build_assign (make_ssa_name (size_type_node),
@@ -378,6 +384,20 @@ pass_heap_optimizations::execute (function *fun)
 
   state.combine_calls ();
 
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); )
+	{
+	  /* Iterate over statements, looking for function calls.  */
+	  gimple *stmt = gsi_stmt (si);
+
+	  if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA))
+	    maybe_optimize_alloca (as_a <gcall *> (stmt));
+
+	  gsi_next (&si);
+	}
+    }
+
   {
       push_cfun (fun);
       dump_function_to_file (fun->decl, stderr, TDF_VOPS);
@@ -425,8 +445,106 @@ pass_heap_optimizations::handle_free (function_state &state, gcall *call)
     }
 }
 
+/* FIXME.
+   Convert alloca calls to something else.
+  
+   If we have:
+
+       ptr = (struct foo *)__builtin_alloca (sizeof (foo));
+
+   convert it to:
+
+       struct foo _obj;
+       ...
+       ptr = &_obj;
+
+   provided that it's safe to do so.
+   TODO: When is it safe?  */
+
+void
+pass_heap_optimizations::maybe_optimize_alloca (gcall *call)
+{
+  warning_at (call->location, 0, "found alloca");
+  // TODO
+
+  tree size = gimple_call_arg (call, 0);
+  debug_tree (size);
+
+  tree result = gimple_call_lhs (call);
+  debug_tree (result);
+
+  /* Must be a fixed-size allocation.  */
+  if (TREE_CODE (size) != INTEGER_CST)
+    return;  
+
+  /* FIXME.  */
+  if (TREE_CODE (TREE_TYPE (result)) != POINTER_TYPE)
+    return;
+
+  tree obj_type = TREE_TYPE (TREE_TYPE (result));
+  debug_tree (obj_type);
+
+  /* Verify that size of allocation is sizeof (OBJ_TYPE).  */
+  //unsigned HOST_WIDE_INT obj_size = ; //tree_to_uhwi (DECL_SIZE_UNIT (obj_type));
+
+#if 0
+  tree obj_size = TREE_CODE (decl) == SSA_NAME
+    ? TYPE_SIZE_UNIT (TREE_TYPE (decl))
+    : DECL_SIZE_UNIT (decl);
+#endif
+
+#if 0
+  unsigned HOST_WIDE_INT alloca_size = tree_to_uhwi (size);
+  unsigned HOST_WIDE_INT obj_size = tree_to_uhwi (TYPE_SIZE (obj_type));
+#endif
+
+  /* FIXME: what other checks?  */
+  // see e.g. in_loop_p within gimple-ssa-warn-alloca.c
+
+  /* All checks passed.  */
+  warning_at (call->location, 0, "converting alloca to local: obj_size: %E bits",
+	      TYPE_SIZE (obj_type));
+
+
+
+  //tree new_local = make_ssa_name (obj_type);
+
+  gimple_seq seq = NULL;
+
+  // FIXME: reuse-lhs
+  //replace_call_with_call_and_fold (gimple_stmt_iterator *gsi, gimple *repl)
+
+  tree new_local = create_tmp_var (obj_type);
+  //tree lhs = make_ssa_name (TREE_TYPE (result));
+
+  tree addr_of_local = build1 (ADDR_EXPR, TREE_TYPE (result), new_local);
+  gimple *new_stmt = gimple_build_assign (result, addr_of_local);
+  //gimple_set_location (stmt, gimple_location (call));
+  //gimple_set_block (stmt, gimple_block (call));
+  gimple_seq_add_stmt (&seq, new_stmt);
+
+  gimple_stmt_iterator gsi = gsi_for_stmt (call);
+  gsi_insert_seq_after (&gsi, seq, GSI_SAME_STMT);
+
+  SSA_NAME_DEF_STMT (result) = new_stmt;
+
+  // FIXME: remove call
+#if 1
+  {
+    gimple_stmt_iterator gsi = gsi_for_stmt (call);  
+    /* bool require_eh_edge_purge = */
+    gsi_remove (&gsi, false /* true*/);
+    unlink_stmt_vdef (call);
+    //release_defs (call);
+  }
+#endif
+
+
+  /* FIXME: add new temporary  */
+  /* Replace call and def_stmt with: ptr = &OBJ;.  */
+  /* Or just let dead-store elimination get it?  */
+}
 
-/* FIXME.  */
 
 #if 0
   /* FIXME:
diff --git a/gcc/passes.def b/gcc/passes.def
index d32e830..2c3b445 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -94,12 +94,13 @@ along with GCC; see the file COPYING3.  If not see
 	  NEXT_PASS (pass_build_ealias);
 	  NEXT_PASS (pass_fre);
 
-          NEXT_PASS (pass_heap_optimizations); /* FIXME: arbitrary place for this.  */
-
 	  NEXT_PASS (pass_early_vrp);
 	  NEXT_PASS (pass_merge_phi);
           NEXT_PASS (pass_dse);
 	  NEXT_PASS (pass_cd_dce);
+
+          NEXT_PASS (pass_heap_optimizations); /* FIXME: arbitrary place for this.  */
+
 	  NEXT_PASS (pass_early_ipa_sra);
 	  NEXT_PASS (pass_tail_recursion);
 	  NEXT_PASS (pass_convert_switch);
diff --git a/gcc/testsuite/gcc.dg/heap-optimization.c b/gcc/testsuite/gcc.dg/heap-optimization.c
index b73c65d..c9f8a03 100644
--- a/gcc/testsuite/gcc.dg/heap-optimization.c
+++ b/gcc/testsuite/gcc.dg/heap-optimization.c
@@ -86,4 +86,173 @@ int unboxing (int x, int y)
 
   return result;
 }
+
+/* PR tree-optimization/21046.  */
+
+extern void foo (char *p, int i);
+
+#if 0
+void hoist_alloc_1 (void)
+{
+  char *p;
+  int i;
+
+  for (i = 0; i < 10; i++)
+    {
+      p = malloc (20);
+      foo (p, i);
+      free (p);
+    }
+}
+
+void hoist_alloc_2 (void)
+{
+  char *p;
+  int i;
+
+  for (i = 0; i < 10; i++)
+    {
+      p = malloc (2 * i + 2);
+      foo (p, i);
+      free (p);
+    }
+}
+#endif
+
+/* PR middle-end/53082.  */
+
+void test_redundant_1 (void)
+{
+  int i;
+  for (i = 0; i < 100; i++)
+    {
+      int *s = (int*) malloc (sizeof(int));
+      *s = 10;
+      free (s);
+    }
+}
+
+void test_combined (void)
+{
+  int i;
+  for (i = 0; i < 100; i++)
+    {
+      int *s1 = malloc (sizeof(int));
+      int *s2 = malloc (sizeof(int));
+      int *s3 = malloc (sizeof(int));
+
+      free(s1); free(s2); free(s3);
+    }
+}
+#endif
+
+#if 1
+// objinst.c
+
+/* -*- mode: c -*-
+ * $Id: objinst.c 36673 2007-05-03 16:55:46Z laurov $
+ * http://www.bagley.org/~doug/shootout/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+enum {false, true};
+
+#define TOGGLE \
+    char state; \
+    char (*value)(struct Toggle *); \
+    struct Toggle *(*activate)(struct Toggle *)
+
+#define DESTROY  free
+
+typedef struct Toggle {
+    TOGGLE;
+} Toggle;
+
+char toggle_value(Toggle *this) {
+    return(this->state);
+}
+Toggle *toggle_activate(Toggle *this) {
+    this->state = !this->state;
+    return(this);
+}
+Toggle *init_Toggle(Toggle *this, char start_state) {
+    this->state = start_state;
+    this->value = toggle_value;
+    this->activate = toggle_activate;
+    return(this);
+}
+Toggle *new_Toggle(char start_state) {
+    Toggle *this = (Toggle *)malloc(sizeof(Toggle));
+    return(init_Toggle(this, start_state));
+}
+
+
+typedef struct NthToggle {
+    Toggle base;
+    int count_max;
+    int counter;
+} NthToggle;
+
+NthToggle *nth_toggle_activate(NthToggle *this) {
+    if (++this->counter >= this->count_max) {
+        this->base.state = !this->base.state;
+        this->counter = 0;
+    }
+    return(this);
+
+}
+NthToggle *init_NthToggle(NthToggle *this, int max_count) {
+    this->count_max = max_count;
+    this->counter = 0;
+    this->base.activate = (Toggle *(*)(Toggle *))nth_toggle_activate;
+    return(this);
+}
+NthToggle *new_NthToggle(char start_state, int max_count) {
+    NthToggle *this = (NthToggle *)malloc(sizeof(NthToggle));
+    this = (NthToggle *)init_Toggle((Toggle *)this, start_state);
+    return(init_NthToggle(this, max_count));
+}
+
+
+int main(int argc, char *argv[]) {
+#ifdef SMALL_PROBLEM_SIZE
+#define LENGTH 7000000
+#else
+#define LENGTH 70000000
+#endif
+    int i, n = ((argc == 2) ? atoi(argv[1]) : LENGTH);
+    Toggle *tog;
+    NthToggle *ntog;
+
+    tog = new_Toggle(true);
+    for (i=0; i<5; i++) {
+        puts((tog->activate(tog)->value(tog)) ? "true" : "false");
+    }
+    DESTROY(tog);
+    for (i=0; i<n; i++) {
+        tog = new_Toggle(true);
+        DESTROY(tog);
+    }
+    
+    puts("");
+
+    ntog = new_NthToggle(true, 3);
+    for (i=0; i<8; i++) {
+        const char *Msg;
+        if (ntog->base.activate((Toggle*)ntog)->value((Toggle*)ntog))
+          Msg = "true";
+        else
+          Msg = "false";
+        puts(Msg);
+    }
+    DESTROY(ntog);
+    for (i=0; i<n; i++) {
+        ntog = new_NthToggle(true, 3);
+        DESTROY(ntog);
+    }
+    return 0;
+}
 #endif
-- 
1.8.5.3

