Field reordering of structs at link-time

2020-11-04  Erick Ochoa  <erick.oc...@theobroma-systems.com>

    * Makefile.in: Add new file to list of sources.
    * common.opt: Add new flag for field reordering.
    * passes.def: Add new pass.
    * tree-pass.h: Same.
    * ipa-field-reorder.c: New file.
    * ipa-type-escape-analysis.c: Export common functions.
    * ipa-type-escape-analysis.h: Same.
---
 gcc/Makefile.in                |   1 +
 gcc/common.opt                 |   4 +
 gcc/ipa-dfe.c                  |  86 ++++-
 gcc/ipa-dfe.h                  |  26 +-
 gcc/ipa-field-reorder.c        | 622 +++++++++++++++++++++++++++++++++
 gcc/ipa-type-escape-analysis.c |  44 ++-
 gcc/ipa-type-escape-analysis.h |  12 +-
 gcc/passes.def                 |   1 +
 gcc/tree-pass.h                |   2 +
 9 files changed, 749 insertions(+), 49 deletions(-)
 create mode 100644 gcc/ipa-field-reorder.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 8ef6047870b..2184bd0fc3d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1417,6 +1417,7 @@ OBJS = \
        internal-fn.o \
        ipa-type-escape-analysis.o \
        ipa-dfe.o \
+       ipa-field-reorder.o \
        ipa-cp.o \
        ipa-sra.o \
        ipa-devirt.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 85351738a29..7885d0f5c0c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -3468,4 +3468,8 @@ Wdfa
 Common Var(warn_dfa) Init(1) Warning
 Warn about dead fields at link time.
 +fipa-field-reorder
+Common Report Var(flag_ipa_field_reorder) Optimization
+Reorder fields.
+
 ; This comment is to ensure we retain the blank line above.
diff --git a/gcc/ipa-dfe.c b/gcc/ipa-dfe.c
index 7c5e81bd6ac..7ab718c3628 100644
--- a/gcc/ipa-dfe.c
+++ b/gcc/ipa-dfe.c
@@ -185,7 +185,7 @@ get_types_replacement (record_field_offset_map_t record_field_offset_map,
 {
   type_stringifier stringifier;
 -  type_reconstructor reconstructor (record_field_offset_map);
+  type_reconstructor reconstructor (record_field_offset_map, "reorg");
   for (std::set<tree>::const_iterator i = to_modify.begin (),
                                            e = to_modify.end ();
        i != e; ++i)
@@ -245,9 +245,9 @@ get_types_replacement (record_field_offset_map_t record_field_offset_map,
  */
 void
 substitute_types_in_program (reorg_record_map_t map,
-                            reorg_field_map_t field_map)
+                            reorg_field_map_t field_map, bool _delete)
 {
-  gimple_type_rewriter rewriter (map, field_map);
+  gimple_type_rewriter rewriter (map, field_map, _delete);
   rewriter.walk ();
   rewriter._rewrite_function_decl ();
 }
@@ -361,8 +361,11 @@ type_reconstructor::set_is_not_modified_yet (tree t)
     return;
    tree type = _reorg_map[tt];
-  const bool is_modified
+  bool is_modified
= strstr (type_stringifier::get_type_identifier (type).c_str (), ".reorg");
+  is_modified
+ |= (bool) strstr (type_stringifier::get_type_identifier (type).c_str (),
+                     ".reorder");
   if (!is_modified)
     return;
 @@ -408,14 +411,20 @@ type_reconstructor::is_memoized (tree t)
   return already_changed;
 }
 -static tree
-get_new_identifier (tree type)
+const char *
+type_reconstructor::get_new_suffix ()
+{
+  return _suffix;
+}
+
+tree
+get_new_identifier (tree type, const char *suffix)
 {
const char *identifier = type_stringifier::get_type_identifier (type).c_str ();
-  const bool is_new_type = strstr (identifier, "reorg");
+  const bool is_new_type = strstr (identifier, suffix);
   gcc_assert (!is_new_type);
   char *new_name;
-  asprintf (&new_name, "%s.reorg", identifier);
+  asprintf (&new_name, "%s.%s", identifier, suffix);
   return get_identifier (new_name);
 }
 @@ -471,7 +480,9 @@ type_reconstructor::_walk_ARRAY_TYPE_post (tree t)
   TREE_TYPE (copy) = build_variant_type_copy (TREE_TYPE (copy));
   copy = is_modified ? build_distinct_type_copy (copy) : copy;
TREE_TYPE (copy) = is_modified ? _reorg_map[TREE_TYPE (t)] : TREE_TYPE (copy); - TYPE_NAME (copy) = is_modified ? get_new_identifier (copy) : TYPE_NAME (copy);
+  TYPE_NAME (copy) = is_modified
+                      ? get_new_identifier (copy, this->get_new_suffix ())
+                      : TYPE_NAME (copy);
   // This is useful so that we go again through type layout
   TYPE_SIZE (copy) = is_modified ? NULL : TYPE_SIZE (copy);
   tree domain = TYPE_DOMAIN (t);
@@ -524,7 +535,9 @@ type_reconstructor::_walk_POINTER_TYPE_post (tree t)
    copy = is_modified ? build_variant_type_copy (copy) : copy;
TREE_TYPE (copy) = is_modified ? _reorg_map[TREE_TYPE (t)] : TREE_TYPE (copy); - TYPE_NAME (copy) = is_modified ? get_new_identifier (copy) : TYPE_NAME (copy);
+  TYPE_NAME (copy) = is_modified
+                      ? get_new_identifier (copy, this->get_new_suffix ())
+                      : TYPE_NAME (copy);
   TYPE_CACHED_VALUES_P (copy) = false;
    tree _t = tree_to_tree (t);
@@ -619,7 +632,8 @@ type_reconstructor::_walk_RECORD_TYPE_post (tree t)
       tree main = TYPE_MAIN_VARIANT (t);
       tree main_reorg = _reorg_map[main];
       tree copy_variant = build_variant_type_copy (main_reorg);
-      TYPE_NAME (copy_variant) = get_new_identifier (copy);
+      TYPE_NAME (copy_variant)
+       = get_new_identifier (copy, this->get_new_suffix ());
       TYPE_SIZE (copy_variant) = NULL;
       TYPE_MAIN_VARIANT (copy_variant) = main_reorg;
       TYPE_SIZE (main_reorg) = NULL;
@@ -631,8 +645,9 @@ type_reconstructor::_walk_RECORD_TYPE_post (tree t)
       // Ok, so now that we have fixed the TYPE_FIELDS of the copy...
       // We need to call layout_type
       copy = is_modified ? build_distinct_type_copy (copy) : copy;
-      TYPE_NAME (copy)
-       = is_modified ? get_new_identifier (copy) : TYPE_NAME (copy);
+      TYPE_NAME (copy) = is_modified
+                          ? get_new_identifier (copy, this->get_new_suffix ())
+                          : TYPE_NAME (copy);
       // This is useful so that we go again through type layout
       TYPE_SIZE (copy) = is_modified ? NULL : TYPE_SIZE (copy);
TYPE_MAIN_VARIANT (copy) = is_modified ? copy : TYPE_MAIN_VARIANT (copy);
@@ -713,6 +728,9 @@ expr_type_rewriter::_walk_PARM_DECL_post (tree t)
 {
   tree temp = tree_to_tree (t);
   tree ttemp = TREE_TYPE (temp);
+  type_stringifier stringifier;
+  const char *name = stringifier.stringify (ttemp).c_str ();
+  log ("relayout parameter declaration %s\n", name);
   const bool is_interesting = is_interesting_type (ttemp);
   if (!is_interesting)
     return;
@@ -749,6 +767,21 @@ expr_type_rewriter::_walk_FUNCTION_DECL_post (tree t)
 void
 expr_type_rewriter::_walk_MEM_REF_post (tree e)
 {
+  tree op0 = TREE_OPERAND (e, 0);
+  tree t2 = TREE_TYPE (op0);
+  const bool in_map2 = _map.find (t2) != _map.end ();
+
+  log ("trying out substituting expression in component_Ref directly\n");
+  type_stringifier stringifier;
+  log ("mem_ref doing weird things maybe %s\n",
+       stringifier.stringify (t2).c_str ());
+  if (in_map2)
+    {
+      log ("success\n");
+      tree r_t = _map[t2];
+      tree _e = tree_to_tree (op0);
+      TREE_TYPE (_e) = r_t;
+    }
   // The second operand is a pointer constant.
   // Its type specifying the type used for type based alias analysis
   tree op1 = TREE_OPERAND (e, 1);
@@ -781,7 +814,8 @@ expr_type_rewriter::_walk_MEM_REF_post (tree e)
     = old_offset / old_type_size_int * reorg_type_size_int + remainder;
    tree new_offset_tree = build_int_cst (TREE_TYPE (op1), new_offset);
-  TREE_OPERAND (e, 1) = new_offset_tree;
+  tree _e = tree_to_tree (e);
+  TREE_OPERAND (_e, 1) = new_offset_tree;
 }
  // TODO:
@@ -803,9 +837,12 @@ expr_type_rewriter::is_interesting_type (tree t)
    // Let's just do a quick sanity check
   tree interesting_type = t;
-  const bool has_valid_suffix
+  bool has_valid_suffix
= strstr (type_stringifier::get_type_identifier (interesting_type).c_str (),
              ".reorg");
+  has_valid_suffix |= (bool)
+ strstr (type_stringifier::get_type_identifier (interesting_type).c_str (),
+           ".reorder");
   gcc_assert (has_valid_suffix);
   return true;
 }
@@ -1027,9 +1064,11 @@ expr_type_rewriter::handle_pointer_arithmetic_constants (gimple *s, tree p,
   tree original_type = _imap[reorg_type];
   // If we are here, that means that our type has the ".reorg" suffix
   // Let's add a sanity check
-  const bool has_suffix
+  bool has_suffix
     = strstr (type_stringifier::get_type_identifier (reorg_type).c_str (),
              ".reorg");
+  has_suffix |= (bool) strstr (
+ type_stringifier::get_type_identifier (reorg_type).c_str (), ".reorder");
   bool is_valid_input = has_suffix;
   gcc_assert (is_valid_input);
 @@ -1084,6 +1123,8 @@ expr_type_rewriter::_walk_post (tree e)
 void
 expr_type_rewriter::_walk_COMPONENT_REF_post (tree e)
 {
+  gcc_assert (e);
+
   tree f = TREE_OPERAND (e, 1);
   // So, what we need is a map between this field and the new field
   const bool in_map = _map2.find (f) != _map2.end ();
@@ -1093,12 +1134,14 @@ expr_type_rewriter::_walk_COMPONENT_REF_post (tree e)
   std::pair<tree, bool> p = _map2[f];
   tree n_f = p.first;
   bool is_deleted = p.second;
-  TREE_OPERAND (e, 1) = n_f;
+  tree _e = tree_to_tree (e);
+  TREE_OPERAND (_e, 1) = n_f;
    if (!is_deleted)
     return;
 -  _delete = true;
+  _delete = _can_delete && true;
+ log ("are we deleting? %s %s\n", _delete ? "t" : "f", is_deleted ? "t" : "f");
 }
  void
@@ -1245,7 +1288,14 @@ gimple_type_rewriter::_walk_pre_gassign (gassign *s)
     {
     case POINTER_PLUS_EXPR:
     case POINTER_DIFF_EXPR:
+      log ("am i handling pointer arithmetic?\n");
+      if (dump_file)
+       print_gimple_stmt (dump_file, s, 0);
+      log ("\n");
       handle_pointer_arithmetic (s);
+      if (dump_file)
+       print_gimple_stmt (dump_file, s, 0);
+      log ("\n");
       break;
     default:
       break;
diff --git a/gcc/ipa-dfe.h b/gcc/ipa-dfe.h
index c92e8592f1f..45a68f9039a 100644
--- a/gcc/ipa-dfe.h
+++ b/gcc/ipa-dfe.h
@@ -69,7 +69,8 @@ typedef std::map<tree, std::pair<tree, bool> > reorg_field_map_t;
 class type_reconstructor : public type_walker
 {
 public:
- type_reconstructor (record_field_offset_map_t records) : _records (records) + type_reconstructor (record_field_offset_map_t records, const char *suffix)
+    : _records (records), _suffix (suffix)
   {};
    /* Whether a type has already been modified.  */
@@ -84,7 +85,8 @@ public:
   /* Map RECORD_TYPE -> is_modified.  */
   typedef std::map<tree, bool> is_modified_map_t;
 -private:
+protected:
+  const char *get_new_suffix ();
    // Modifications to the current sub_type
   std::stack<tree> in_progress;
@@ -105,6 +107,9 @@ private:
   // Which records can be modified.
   record_field_offset_map_t _records;
 +  // The new suffix
+  const char *_suffix;
+
   // Which fields will be deleted.
   field_tuple_list_stack_t field_list_stack;
 @@ -127,6 +132,7 @@ private:
   // If the type has been modified.
   bool get_is_modified (tree);
 +private:
   // Compute new FIELD_DECL list.
   virtual void _walk_field_pre (tree);
   virtual void _walk_field_post (tree);
@@ -150,8 +156,9 @@ private:
 class expr_type_rewriter : public expr_walker
 {
 public:
-  expr_type_rewriter (reorg_record_map_t map, reorg_field_map_t map2)
-    : _delete (false), _map (map), _map2 (map2)
+  expr_type_rewriter (reorg_record_map_t map, reorg_field_map_t map2,
+                   bool can_delete)
+    : _delete (false), _can_delete (can_delete), _map (map), _map2 (map2)
   {
     /* Create an inverse map new RECORD_TYPE -> old RECORD_TYPE.  */
     for (reorg_record_map_t::iterator i = map.begin (),
@@ -178,6 +185,7 @@ public:
   // Delete statement.
   bool delete_statement ();
   bool _delete;
+  bool _can_delete;
  private:
   // Old RECORD_TYPE -> new RECORD_TYPE.
@@ -207,8 +215,9 @@ private:
 class gimple_type_rewriter : public gimple_walker
 {
 public:
-  gimple_type_rewriter (reorg_record_map_t map, reorg_field_map_t map2)
-    : exprTypeRewriter (map, map2)
+  gimple_type_rewriter (reorg_record_map_t map, reorg_field_map_t map2,
+                     bool can_delete)
+    : exprTypeRewriter (map, map2, can_delete)
   {};
    void _rewrite_function_decl ();
@@ -242,6 +251,9 @@ get_types_replacement (record_field_offset_map_t record_field_offset_map,
 // Substitute types.
 void
 substitute_types_in_program (reorg_record_map_t map,
-                            reorg_field_map_t field_map);
+                            reorg_field_map_t field_map, bool _delete);
+
+tree
+get_new_identifier (tree type, const char *suffix);
  #endif /* GCC_IPA_DFE */
diff --git a/gcc/ipa-field-reorder.c b/gcc/ipa-field-reorder.c
new file mode 100644
index 00000000000..e1094efe934
--- /dev/null
+++ b/gcc/ipa-field-reorder.c
@@ -0,0 +1,622 @@
+/* IPA Type Escape Analysis and Dead Field Elimination
+   Copyright (C) 2019-2020 Free Software Foundation, Inc.
+
+  Contributed by Erick Ochoa <erick.oc...@theobroma-systems.com>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural field reordering
+
+   The goal of this transformation is to re-order fields in structures
+   at link time.
+
+   The field reordering transformation allows to reduce the memory
+ footprint of structs, which not only improves performance, but also memory
+   consumption.  So this is an interesting feature on embedded systems with
+   tight memory constraints.
+
+   First stage - DFA
+   =================
+
+   Use DFA to compute the set of FIELD_DECLs which can be reordered.
+ This is different from IPA-DFE in that all reads do not prevent reordering
+   of fields, with the exception of those which take the address of a field
+   or those in MEM_REF.  Therefore ExprEscaper remains the same, but
+   GimpleCaster is modified.
+
+   Second stage - Reorder fields
+   =============================
+
+   Use TypeReconstructorFieldReordering to reorder fields.
+
+   Third stage - Substitute Types and Relayout
+   ===========================================
+
+   Change all types changed and references to FIELD_DECLs
+ */
+
+#include "config.h"
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+#include <stack>
+#include <algorithm>
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "predict.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "fold-const.h"
+#include "gimple-fold.h"
+#include "symbol-summary.h"
+#include "tree-vrp.h"
+#include "ipa-prop.h"
+#include "tree-pretty-print.h"
+#include "tree-inline.h"
+#include "ipa-fnsummary.h"
+#include "ipa-utils.h"
+#include "tree-ssa-ccp.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "basic-block.h" //needed for gimple.h
+#include "function.h"    //needed for gimple.h
+#include "gimple.h"
+#include "stor-layout.h"
+#include "cfg.h" // needed for gimple-iterator.h
+#include "gimple-iterator.h"
+#include "gimplify.h"      //unshare_expr
+#include "value-range.h"   // make_ssa_name dependency
+#include "tree-ssanames.h" // make_ssa_name
+#include "ssa.h"
+#include "tree-into-ssa.h"
+#include "gimple-ssa.h" // update_stmt
+#include "tree.h"
+#include "gimple-expr.h"
+#include "predict.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "fold-const.h"
+#include "gimple-fold.h"
+#include "symbol-summary.h"
+#include "tree-vrp.h"
+#include "ipa-prop.h"
+#include "tree-pretty-print.h"
+#include "tree-inline.h"
+#include "ipa-fnsummary.h"
+#include "ipa-utils.h"
+#include "tree-ssa-ccp.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "tree-ssa-alias.h"
+#include "tree-ssanames.h"
+#include "gimple.h"
+#include "cfg.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "gimple-pretty-print.h"
+
+#include "ipa-type-escape-analysis.h"
+#include "ipa-dfe.h"
+
+// TODO:
+// This was copy pasted from tree-ssa-structalias.c
+// Maybe delete this and make the function visible?
+static HOST_WIDE_INT
+bitpos_of_field (const tree fdecl)
+{
+  if (!tree_fits_shwi_p (DECL_FIELD_OFFSET (fdecl))
+      || !tree_fits_shwi_p (DECL_FIELD_BIT_OFFSET (fdecl)))
+    return -1;
+
+  return (tree_to_shwi (DECL_FIELD_OFFSET (fdecl)) * BITS_PER_UNIT
+         + tree_to_shwi (DECL_FIELD_BIT_OFFSET (fdecl)));
+}
+
+/* Basically we are creating a specialized GimpleAccesser for FieldReordering. + * Here instead of marking fields as "READ" or "WRITE", we are marking them as + * "READ" via pointer arithmetic. Other "READS" and "WRITES" are ignored since
+ * it would be possible to reorder the fields.
+ */
+class GimpleAccesserFieldReordering : public gimple_accessor
+{
+public:
+  GimpleAccesserFieldReordering ()
+  {};
+
+private:
+  /* Mark all RHS expressions reachable from S as Read.
+     all all LHS expressions reachable from S as Written.  */
+  virtual void _walk_pre_gcall (gcall *s);
+
+  /* Mark all RHS expressions reachable from S as Read.
+     Mark all LHS expressions reachable from S as Written.  */
+  virtual void _walk_pre_gassign (gassign *s);
+
+  /* Mark all all expressions reachable from S as read.  */
+  virtual void _walk_pre_greturn (greturn *s);
+
+  /* Mark all expressions reachable from S as read.  */
+  virtual void _walk_pre_gcond (gcond *s);
+
+  // Do we need a glabel? I don't think so...
+  // But we might need a gswitch.
+};
+
+/* Class used to create new types derived from types that might be
+ * reordered.
+ */
+class TypeReconstructorFieldReordering : public type_reconstructor
+{
+public:
+  TypeReconstructorFieldReordering (record_field_offset_map_t records,
+                                   const char *suffix)
+    : type_reconstructor (records, suffix)
+  {};
+
+private:
+  // Compute new RECORD_TYPE.
+  virtual void _walk_RECORD_TYPE_post (tree);
+};
+
+/* Compare FIELD_DECL tree based on TYPE_SIZE unit. */
+static bool
+compare_FIELD_DECLs_TYPE_SIZE (tree _l, tree _r)
+{
+  gcc_assert (_l && _r);
+
+  tree l = tree_to_tree (_l);
+  tree r = tree_to_tree (_r);
+
+  const enum tree_code code_l = TREE_CODE (l);
+  const enum tree_code code_r = TREE_CODE (r);
+  const bool is_left_field_decl = code_l == FIELD_DECL;
+  const bool is_right_field_decl = code_r == FIELD_DECL;
+  bool is_valid = is_left_field_decl && is_right_field_decl;
+  gcc_assert (is_valid);
+
+  tree type_l = TREE_TYPE (l);
+  tree type_r = TREE_TYPE (r);
+  const bool is_type_l = TYPE_P (type_l);
+  const bool is_type_r = TYPE_P (type_r);
+  is_valid = is_type_l && is_type_r;
+  gcc_assert (is_valid);
+
+  tree size_l = TYPE_SIZE_UNIT (type_l);
+  tree size_r = TYPE_SIZE_UNIT (type_r);
+  is_valid = size_l && size_r;
+  gcc_assert (is_valid);
+
+  int int_l = tree_to_shwi (size_l);
+  int int_r = tree_to_shwi (size_r);
+  const bool is_gte_l = int_l >= 0;
+  const bool is_gte_r = int_r >= 0;
+  is_valid = is_gte_l && is_gte_r;
+  gcc_assert (is_valid);
+
+  return int_l > int_r;
+}
+
+void
+TypeReconstructorFieldReordering::_walk_RECORD_TYPE_post (tree t)
+{
+  tree t2 = for_reference.top ();
+  gcc_assert (t2 == t);
+  for_reference.pop ();
+
+  tree copy = in_progress.top ();
+  in_progress.pop ();
+  field_tuple_list_t field_tuple_list = field_list_stack.top ();
+  field_list_stack.pop ();
+
+  // So, here all the work has been done to make sure
+  // that the fields produced a field_tuple_list_t
+  // with old fields and pointers to new fields.
+  // There might be NULL values if new fields are that can be resorted..
+  // So, now we want to do a couple of things.
+  // First, collect all fields in a struct and make a copy of them
+  bool is_modified = get_is_modified (t);
+  std::vector<tree> to_reorder;
+  is_modified = true;
+  for (field_tuple_list_t::iterator i = field_tuple_list.begin (),
+       e = field_tuple_list.end ();
+       i != e; ++i)
+    {
+      field_tuple_t field_tuple = *i;
+      tree modified_field = field_tuple.second;
+
+      if (!modified_field)
+       {
+         // This means that the field can be re-ordered...
+         is_modified = true;
+       }
+
+      log ("we can reorder %s ? %s\n",
+          type_stringifier::get_field_identifier (field_tuple.first).c_str (),
+          !modified_field ? "true" : "false");
+      to_reorder.push_back (
+       (tree) copy_node (tree_to_tree (field_tuple.first)));
+    }
+
+  if (is_modified)
+    {
+      tree prev_field = NULL;
+      bool entered_loop = false;
+      // Sort them
+      std::sort (to_reorder.begin (), to_reorder.end (),
+                compare_FIELD_DECLs_TYPE_SIZE);
+      is_modified = false;
+
+      for (field_tuple_list_t::iterator i = field_tuple_list.begin (),
+          e = field_tuple_list.end ();
+          i != e; ++i)
+       {
+         field_tuple_t field_tuple = *i;
+         tree modified_field = field_tuple.second;
+
+         if (!modified_field)
+           {
+             // This means that the field can be re-ordered...
+             is_modified = true;
+           }
+
+         tree current_field = modified_field;
+         if (!is_modified)
+           {
+             prev_field = current_field;
+             continue;
+           }
+
+         gcc_assert (!modified_field && is_modified);
+         // Create new TYPE_FIELDS with the order we want
+         for (std::vector<tree>::iterator j = to_reorder.begin (),
+               f = to_reorder.end (); j != f; ++j)
+           {
+             entered_loop = true;
+             tree current_field_inner = *j;
+             if (!prev_field)
+               {
+                 TYPE_FIELDS (copy) = tree_to_tree (current_field_inner);
+               }
+             else
+               {
+                 DECL_CHAIN (prev_field)
+                   = tree_to_tree (current_field_inner);
+               }
+
+             prev_field = tree_to_tree (current_field_inner);
+           }
+
+         if (entered_loop)
+           break;
+       }
+
+      // Modify _reorg_fields map
+      for (std::vector<tree>::iterator i = to_reorder.begin (),
+               e = to_reorder.end (); i != e; ++i)
+       {
+         tree to_find = *i;
+         unsigned to_find_i = bitpos_of_field (tree_to_tree (to_find));
+         const char *to_find_str
+           = type_stringifier::get_field_identifier (to_find).c_str ();
+         // O^2 for now but an improvement can be to change this
+         for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
+           {
+             // safe for anonymous structs
+             const char *haystack
+               = type_stringifier::get_field_identifier (field).c_str ();
+             unsigned haystack_i = bitpos_of_field (field);
+             if (haystack_i == to_find_i)
+               {
+                 _reorg_fields[field]
+                   = std::make_pair (tree_to_tree (to_find), false);
+                 log ("substituting %s for %s\n", to_find_str, haystack);
+               }
+           }
+       }
+    }
+
+  const bool is_main_variant = TYPE_MAIN_VARIANT (t) == t;
+  // We already must have done the main variant...
+  if (!is_main_variant)
+    {
+      tree main = TYPE_MAIN_VARIANT (t);
+      tree main_reorg = _reorg_map[main];
+      tree copy_variant = build_distinct_type_copy (main_reorg);
+      TYPE_NAME (copy_variant)
+       = get_new_identifier (copy, this->get_new_suffix ());
+      TYPE_SIZE (copy_variant) = NULL;
+      TYPE_ALIAS_SET (copy_variant) = -1;
+      layout_type (copy_variant);
+      TYPE_MAIN_VARIANT (copy_variant) = main_reorg;
+      _reorg_map[t] = copy_variant;
+    }
+  else
+    {
+      // Ok, so now that we have fixed the TYPE_FIELDS of the copy...
+      // We need to call layout_type
+      copy = is_modified ? build_distinct_type_copy (copy) : copy;
+      TYPE_NAME (copy) = is_modified
+                          ? get_new_identifier (copy, this->get_new_suffix ())
+                          : TYPE_NAME (copy);
+      // This is useful so that we go again through type layout
+      TYPE_SIZE (copy) = is_modified ? NULL : TYPE_SIZE (copy);
+ TYPE_MAIN_VARIANT (copy) = is_modified ? copy : TYPE_MAIN_VARIANT (copy);
+      if (is_modified)
+       layout_type (copy);
+      tree _t = tree_to_tree (t);
+      _reorg_map[t] = is_modified ? copy : _t;
+    }
+
+  tree record = _reorg_map[t];
+ for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field))
+    {
+      relayout_decl (field);
+      log ("new offset for %s %d\n",
+          type_stringifier::get_field_identifier (field).c_str (),
+          bitpos_of_field (field));
+    }
+}
+
+/* Mark all as empty (a.k.a. we can sort) */
+void
+GimpleAccesserFieldReordering::_walk_pre_gassign (gassign *s)
+{
+  // There seems to be quite a bit of code duplication here...
+  const enum gimple_rhs_class code = gimple_assign_rhs_class (s);
+  switch (code)
+    {
+    case GIMPLE_TERNARY_RHS:
+      {
+       tree rhs3 = gimple_assign_rhs3 (s);
+       gcc_assert (rhs3);
+       _expr_accessor.update (rhs3, Empty);
+      }
+    /* fall-through */
+    case GIMPLE_BINARY_RHS:
+      {
+       tree rhs2 = gimple_assign_rhs2 (s);
+       gcc_assert (rhs2);
+       _expr_accessor.update (rhs2, Empty);
+      }
+    /* fall-through */
+    case GIMPLE_UNARY_RHS:
+    case GIMPLE_SINGLE_RHS:
+      {
+       tree rhs1 = gimple_assign_rhs1 (s);
+       _expr_accessor.update (rhs1, Empty);
+       tree lhs = gimple_assign_lhs (s);
+       if (!lhs)
+         break;
+       _expr_accessor.update (lhs, Empty);
+       break;
+      }
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Mark all as empty (a.k.a. we can sort) */
+void
+GimpleAccesserFieldReordering::_walk_pre_gcall (gcall *s)
+{
+  unsigned n = gimple_call_num_args (s);
+  for (unsigned i = 0; i < n; i++)
+    {
+      tree a = gimple_call_arg (s, i);
+      gcc_assert (a);
+      _expr_accessor.update (a, Empty);
+    }
+
+  tree lhs = gimple_call_lhs (s);
+  if (!lhs)
+    return;
+  _expr_accessor.update (lhs, Empty);
+}
+
+/* Mark all as empty (a.k.a. we can sort) */
+void
+GimpleAccesserFieldReordering::_walk_pre_greturn (greturn *s)
+{
+  tree val = gimple_return_retval (s);
+  if (!val)
+    return;
+  _expr_accessor.update (val, Empty);
+}
+
+/* Mark all as empty (a.k.a. we can sort) */
+void
+GimpleAccesserFieldReordering::_walk_pre_gcond (gcond *s)
+{
+  tree lhs = gimple_cond_lhs (s);
+  tree rhs = gimple_cond_rhs (s);
+  gcc_assert (lhs && rhs);
+  _expr_accessor.update (lhs, Empty);
+  _expr_accessor.update (rhs, Empty);
+}
+
+/* Top level function.  */
+static unsigned int
+lto_fr_execute ();
+
+static record_field_map_t
+find_fields_accessed ();
+
+namespace {
+const pass_data pass_data_ipa_field_reorder = {
+  SIMPLE_IPA_PASS,
+  "field-reorder",
+  OPTGROUP_NONE,
+  TV_NONE,
+  (PROP_cfg | PROP_ssa),
+  0,
+  0,
+  0,
+  0,
+};
+
+class pass_ipa_field_reorder : public simple_ipa_opt_pass
+{
+public:
+  pass_ipa_field_reorder (gcc::context *ctx)
+    : simple_ipa_opt_pass (pass_data_ipa_field_reorder, ctx)
+  {}
+
+  virtual bool gate (function *)
+  {
+    return in_lto_p && flag_ipa_field_reorder;
+  }
+  virtual unsigned execute (function *)
+  {
+    return lto_fr_execute ();
+  }
+};
+} // namespace
+
+record_field_map_t static find_fields_accessed ()
+{
+  GimpleAccesserFieldReordering accesser;
+  accesser.walk ();
+
+  // This record_field_map holds
+  // RECORD_TYPE -> (FIELD_DECL -> how field is accessed)
+  record_field_map_t record_field_map = accesser.get_map ();
+  return record_field_map;
+}
+
+/* record_field_offset_map holds information on which FIELD_DECLs might be
+ * resorted from RECORD_TYPEs.  to_modify holds trees which point to a
+ * RECORD_TYPE which might be resorted.  From these two inputs, we need to
+ * compute two maps:
+ * * a map of RECORD_TYPE (old) -> RECORD_TYPE (new)
+ * * a map of FIELD_DECL (old) -> FIELD_DECL (new)
+
+ * The first maps trees in to_modify (which point to RECORD_TYPEs (old)) to
+ * trees which have been modified to point to the new definition of
+ * RECORD_TYPEs.
+
+ * The second maps old FIELD_DECLs trees to the new FIELD_DECLs.
+ */
+reorg_maps_t
+get_reordered_field_maps (record_field_offset_map_t record_field_offset_map,
+                         std::set<tree> to_modify)
+{
+  type_stringifier stringifier;
+
+  TypeReconstructorFieldReordering reconstructor (record_field_offset_map,
+                                                 "reorder");
+  for (std::set<tree>::const_iterator i = to_modify.begin (),
+                                           e = to_modify.end ();
+       i != e; ++i)
+    {
+      tree record = *i;
+      reconstructor.walk (TYPE_MAIN_VARIANT (record));
+    }
+
+  for (std::set<tree>::const_iterator i = to_modify.begin (),
+                                           e = to_modify.end ();
+       i != e; ++i)
+    {
+      tree record = *i;
+      reconstructor.walk (record);
+    }
+
+  reorg_record_map_t map = reconstructor.get_map ();
+  reorg_field_map_t field_map = reconstructor.get_field_map ();
+
+ // Here, we are just making sure that we are not doing anything too crazy.
+  // Also, we found some types for which TYPE_CACHED_VALUES_P is not being
+  // rewritten.  This is probably indicative of a bug in
+  // TypeReconstructorFieldReordering.
+  for (std::map<tree, tree>::const_iterator i = map.begin (),
+                                                 e = map.end ();
+       i != e; ++i)
+    {
+      tree o_record = i->first;
+      std::string o_name = stringifier.stringify (o_record);
+      log ("original: %s\n", o_name.c_str ());
+      tree r_record = i->second;
+      std::string r_name
+       = r_record ? stringifier.stringify (r_record) : std::string ("");
+      log ("modified: %s\n", r_name.c_str ());
+      if (!r_record)
+       continue;
+      tree m_record = TYPE_MAIN_VARIANT (r_record);
+      // Info: We had a bug where some TYPED_CACHED_VALUES were preserved?
+      tree _o_record = tree_to_tree (o_record);
+      TYPE_CACHED_VALUES_P (_o_record) = false;
+      TYPE_CACHED_VALUES_P (m_record) = false;
+
+      bool in_map = map.find (m_record) != map.end ();
+      if (!in_map)
+       continue;
+      tree mm_record = map[m_record];
+      // Info: I think this is no longer needed...
+      // Please verify
+      TYPE_MAIN_VARIANT (r_record) = mm_record;
+    }
+
+  return std::make_pair (map, field_map);
+}
+
+/* Top level function.  */
+static unsigned int
+lto_fr_execute ()
+{
+  log ("here in field reordering \n");
+  // Analysis.
+  tpartitions_t escaping_nonescaping_sets
+    = partition_types_into_escaping_nonescaping ();
+  record_field_map_t record_field_map = find_fields_accessed ();
+  record_field_offset_map_t record_field_offset_map
+    = obtain_nonescaping_unaccessed_fields (escaping_nonescaping_sets,
+                                           record_field_map, 0);
+
+  if (record_field_offset_map.empty ())
+    return 0;
+
+  // Prepare for transformation.
+  std::set<tree> to_modify
+    = get_all_types_pointing_to (record_field_offset_map,
+                                escaping_nonescaping_sets);
+
+  reorg_maps_t replacements
+    = get_reordered_field_maps (record_field_offset_map, to_modify);
+  reorg_record_map_t map = replacements.first;
+  reorg_field_map_t field_map = replacements.second;
+  substitute_types_in_program (map, field_map, false);
+
+  gimple_walker walker;
+  walker.walk ();
+  log ("finished!\n");
+
+  return 0;
+}
+
+simple_ipa_opt_pass *
+make_pass_ipa_field_reorder (gcc::context *ctx)
+{
+  return new pass_ipa_field_reorder (ctx);
+}
diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c
index 579a98d5720..f142b6e51ca 100644
--- a/gcc/ipa-type-escape-analysis.c
+++ b/gcc/ipa-type-escape-analysis.c
@@ -166,6 +166,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-iterator.h"
 #include "gimple-ssa.h"
 #include "gimple-pretty-print.h"
+#include "tree-cfg.h"
  #include "ipa-type-escape-analysis.h"
 #include "ipa-dfe.h"
@@ -178,10 +179,6 @@ lto_dfe_execute ();
 static tpartitions_t
 partition_types_into_record_reaching_or_non_record_reaching ();
 -// Partition types into escaping or non escaping sets.
-static tpartitions_t
-partition_types_into_escaping_nonescaping ();
-
 // Perform dead field elimination.
 static void
 lto_dead_field_elimination ();
@@ -194,11 +191,6 @@ fix_escaping_types_in_set (tpartitions_t &types);
 static record_field_map_t
 find_fields_accessed ();
 -// Obtain intersection of unaccessed and non escaping types.
-static record_field_offset_map_t
-obtain_nonescaping_unaccessed_fields (tpartitions_t casting,
-                                     record_field_map_t record_field_map);
-
 // TODO:
 // This was copy pasted from tree-ssa-structalias.c
 // Maybe delete this and make the function visible?
@@ -275,7 +267,7 @@ lto_dead_field_elimination ()
   record_field_map_t record_field_map = find_fields_accessed ();
   record_field_offset_map_t record_field_offset_map
     = obtain_nonescaping_unaccessed_fields (escaping_nonescaping_sets,
-                                           record_field_map);
+                                           record_field_map, OPT_Wdfa);
   if (record_field_offset_map.empty ())
     return;
 @@ -288,8 +280,7 @@ lto_dead_field_elimination ()
   reorg_record_map_t map = replacements.first;
   reorg_field_map_t field_map = replacements.second;
   // Transformation.
-  substitute_types_in_program (map, field_map);
-
+  substitute_types_in_program (map, field_map, true);
 }
  /* Iterate all gimple bodies and collect trees
@@ -310,7 +301,7 @@ partition_types_into_record_reaching_or_non_record_reaching ()
 /* Iterate over all gimple bodies and find out
  * which types are escaping AND are being casted.
  */
-static tpartitions_t
+tpartitions_t
 partition_types_into_escaping_nonescaping ()
 {
   tpartitions_t partitions
@@ -330,8 +321,7 @@ partition_types_into_escaping_nonescaping ()
  * which fields are accessed for all RECORD_TYPE
  * types.
  */
-static record_field_map_t
-find_fields_accessed ()
+record_field_map_t static find_fields_accessed ()
 {
   gimple_accessor accesser;
   accesser.walk ();
@@ -497,9 +487,10 @@ mark_escaping_types_to_be_deleted (
 }
  // Obtain nonescaping unaccessed fields
-static record_field_offset_map_t
+record_field_offset_map_t
 obtain_nonescaping_unaccessed_fields (tpartitions_t casting,
-                                     record_field_map_t record_field_map)
+                                     record_field_map_t record_field_map,
+                                     int _warning)
 {
   bool has_fields_that_can_be_deleted = false;
   record_field_offset_map_t record_field_offset_map;
@@ -559,10 +550,11 @@ obtain_nonescaping_unaccessed_fields (tpartitions_t casting,
               type_stringifier::get_type_identifier (record).c_str (),
               type_stringifier::get_field_identifier (field).c_str ());
 -        if (OPT_Wdfa == 0) continue;
+         if (_warning == 0)
+           continue;
          // Anonymous fields? (Which the record can be!).
-           warning (OPT_Wdfa, "RECORD_TYPE %qE has dead field %qE in LTO.\n",
-               record, field);
+         warning (_warning, "RECORD_TYPE %qE has dead field %qE in LTO.\n",
+                  record, field);
        }
       record_field_offset_map[record] = field_offset;
     }
@@ -1181,6 +1173,8 @@ gimple_walker::walk ()
       if (already_in_set)
        continue;
 +      if (dump_file)
+       dump_function_to_file (node->decl, dump_file, TDF_NONE);
       _walk_cnode (node);
       fndecls.insert (decl);
     }
@@ -1402,8 +1396,8 @@ gimple_walker::_walk_gimple (gimple *stmt)
   const enum gimple_code code = gimple_code (stmt);
   switch (code)
     {
-    case GIMPLE_PREDICT:
     case GIMPLE_DEBUG:
+    case GIMPLE_PREDICT:
     case GIMPLE_NOP:
       // I think it is safe to skip these
       // but it would also be nice to walk their sub-trees
@@ -2656,9 +2650,13 @@ expr_accessor::_walk_ADDR_EXPR_pre (__attribute__ ((unused)) tree e) for (field = TYPE_FIELDS (addr_expr_t); field; field = DECL_CHAIN (field))
     {
       log ("ever inside?\n");
+      // This is a weird result...
       unsigned f_byte_offset = tree_to_uhwi (DECL_FIELD_OFFSET (field));
-      unsigned f_offset = f_byte_offset;
- log ("offset field %d, offset by pointer %d\n", f_offset, offset_int); + unsigned f_bit_offset = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)) / 8;
+      unsigned f_offset = f_byte_offset + f_bit_offset;
+      // unsigned f_offset = bitpos_of_field (field);
+      log ("offset field %d %d, offset by pointer %d \n ", f_offset,
+          f_bit_offset, offset_int);
        // NOTE: ALL FIELDS BEFORE THIS ONE NEED TO EXIST
       // Otherwise, this pointer arithmetic is invalid...
diff --git a/gcc/ipa-type-escape-analysis.h b/gcc/ipa-type-escape-analysis.h
index d64268308f9..0bf07cc3928 100644
--- a/gcc/ipa-type-escape-analysis.h
+++ b/gcc/ipa-type-escape-analysis.h
@@ -1132,10 +1132,11 @@ public:
   /* Get final results.  */
   record_field_map_t get_map ();
 -private:
+protected:
   /* Navigate expressions in gimple statements.  */
   expr_accessor _expr_accessor;
 +private:
   /* Mark all RHS expressions reachable from S as Read.
      all all LHS expressions reachable from S as Written.  */
   virtual void _walk_pre_gcall (gcall *s);
@@ -1158,5 +1159,14 @@ typedef std::set<unsigned> field_offsets_t;
  typedef std::map<tree, field_offsets_t> record_field_offset_map_t;
 +// Partition types into escaping or non escaping sets.
+tpartitions_t
+partition_types_into_escaping_nonescaping ();
+
+// Compute set of not escaping unaccessed fields
+record_field_offset_map_t
+obtain_nonescaping_unaccessed_fields (tpartitions_t casting,
+                                     record_field_map_t record_field_map,
+                                     int warning);
  #endif /* GCC_IPA_TYPE_ESCAPE_ANALYSIS_H */
diff --git a/gcc/passes.def b/gcc/passes.def
index a1cf09229d6..156421aa837 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -173,6 +173,7 @@ along with GCC; see the file COPYING3.  If not see
      compiled unit.  */
   INSERT_PASSES_AFTER (all_late_ipa_passes)
   NEXT_PASS (pass_ipa_type_escape_analysis);
+  NEXT_PASS (pass_ipa_field_reorder);
   NEXT_PASS (pass_ipa_pta);
   NEXT_PASS (pass_omp_simd_clone);
   TERMINATE_PASS_LIST (all_late_ipa_passes)
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index f1a7dc6758e..4392e099359 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -517,6 +517,8 @@ extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
 extern simple_ipa_opt_pass *
 make_pass_ipa_type_escape_analysis (gcc::context *ctxt);
+extern simple_ipa_opt_pass *
+make_pass_ipa_field_reorder (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_target_clone (gcc::context *ctxt);
--
2.18.1

Reply via email to