1192 lines
38 KiB
Diff
1192 lines
38 KiB
Diff
From 7930d75c9fd3f36cc2dce934569f00c71248bb31 Mon Sep 17 00:00:00 2001
|
|
From: liyancheng <412998149@qq.com>
|
|
Date: Sat, 25 Nov 2023 10:28:48 +0800
|
|
Subject: [PATCH] [Struct Reorg] Add Safe Structure Pointer Compression
|
|
|
|
Safe structure pointer compression allows safely transfer pointers
|
|
stored in structure into the index of structure array with smaller
|
|
type to reduce the size of structure.
|
|
Add flag -fipa-struct-reorg=4 to enable safe structure pointer
|
|
compression.
|
|
Add param compressed-pointer-size=[8,16,32] to control the compressed
|
|
pointer size.
|
|
---
|
|
gcc/common.opt | 5 +-
|
|
gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 908 ++++++++++++++++++++++-
|
|
gcc/ipa-struct-reorg/ipa-struct-reorg.h | 4 +
|
|
gcc/params.opt | 4 +
|
|
4 files changed, 882 insertions(+), 39 deletions(-)
|
|
|
|
diff --git a/gcc/common.opt b/gcc/common.opt
|
|
index b01df919e..f6e20c1e8 100644
|
|
--- a/gcc/common.opt
|
|
+++ b/gcc/common.opt
|
|
@@ -1993,8 +1993,9 @@ Common Var(flag_ipa_struct_reorg) Init(0) Optimization
|
|
Perform structure layout optimizations.
|
|
|
|
fipa-struct-reorg=
|
|
-Common RejectNegative Joined UInteger Var(struct_layout_optimize_level) Init(0) IntegerRange(0, 3)
|
|
--fipa-struct-reorg=[0,1,2,3] adding none, struct-reorg, reorder-fields, dfe optimizations.
|
|
+Common RejectNegative Joined UInteger Var(struct_layout_optimize_level) Init(0) IntegerRange(0, 4)
|
|
+-fipa-struct-reorg=[0,1,2,3,4] adding none, struct-reorg, reorder-fields,
|
|
+dfe, safe-pointer-compression optimizations.
|
|
|
|
fipa-vrp
|
|
Common Var(flag_ipa_vrp) Optimization
|
|
diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc
|
|
index dcc6df496..5d451c4c8 100644
|
|
--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc
|
|
+++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc
|
|
@@ -89,6 +89,7 @@ along with GCC; see the file COPYING3. If not see
|
|
#include "gimple-iterator.h"
|
|
#include "gimple-walk.h"
|
|
#include "cfg.h"
|
|
+#include "cfghooks.h" /* For split_block. */
|
|
#include "ssa.h"
|
|
#include "tree-dfa.h"
|
|
#include "fold-const.h"
|
|
@@ -147,7 +148,27 @@ using namespace struct_relayout;
|
|
#define VOID_POINTER_P(type) \
|
|
(POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type)))
|
|
|
|
-/* Return true iff TYPE is stdarg va_list type. */
|
|
+static void
|
|
+set_var_attributes (tree var)
|
|
+{
|
|
+ if (!var)
|
|
+ return;
|
|
+ gcc_assert (TREE_CODE (var) == VAR_DECL);
|
|
+
|
|
+ DECL_ARTIFICIAL (var) = 1;
|
|
+ DECL_EXTERNAL (var) = 0;
|
|
+ TREE_STATIC (var) = 1;
|
|
+ TREE_PUBLIC (var) = 0;
|
|
+ TREE_USED (var) = 1;
|
|
+ DECL_CONTEXT (var) = NULL;
|
|
+ TREE_THIS_VOLATILE (var) = 0;
|
|
+ TREE_ADDRESSABLE (var) = 0;
|
|
+ TREE_READONLY (var) = 0;
|
|
+ if (is_global_var (var))
|
|
+ set_decl_tls_model (var, TLS_MODEL_NONE);
|
|
+}
|
|
+
|
|
+/* Return true if TYPE is stdarg va_list type. */
|
|
|
|
static inline bool
|
|
is_va_list_type (tree type)
|
|
@@ -271,9 +292,15 @@ enum struct_layout_opt_level
|
|
STRUCT_SPLIT = 1 << 0,
|
|
COMPLETE_STRUCT_RELAYOUT = 1 << 1,
|
|
STRUCT_REORDER_FIELDS = 1 << 2,
|
|
- DEAD_FIELD_ELIMINATION = 1 << 3
|
|
+ DEAD_FIELD_ELIMINATION = 1 << 3,
|
|
+ POINTER_COMPRESSION_SAFE = 1 << 4
|
|
};
|
|
|
|
+/* Defines the target pointer size of compressed pointer, which should be 8,
|
|
+ 16, 32. */
|
|
+
|
|
+static int compressed_size = 32;
|
|
+
|
|
static bool is_result_of_mult (tree arg, tree *num, tree struct_size);
|
|
static bool isptrptr (tree type);
|
|
void get_base (tree &base, tree expr);
|
|
@@ -394,7 +421,10 @@ srtype::srtype (tree type)
|
|
: type (type),
|
|
chain_type (false),
|
|
escapes (does_not_escape),
|
|
+ pc_gptr (NULL_TREE),
|
|
visited (false),
|
|
+ pc_candidate (false),
|
|
+ has_legal_alloc_num (false),
|
|
has_alloc_array (0)
|
|
{
|
|
for (int i = 0; i < max_split; i++)
|
|
@@ -476,6 +506,31 @@ srtype::mark_escape (escape_type e, gimple *stmt)
|
|
}
|
|
}
|
|
|
|
+/* Create a global header for compressed struct. */
|
|
+
|
|
+void
|
|
+srtype::create_global_ptr_for_pc ()
|
|
+{
|
|
+ if (!pc_candidate || pc_gptr != NULL_TREE)
|
|
+ return;
|
|
+
|
|
+ const char *type_name = get_type_name (type);
|
|
+ gcc_assert (type_name != NULL);
|
|
+
|
|
+ char *gptr_name = concat (type_name, "_pc", NULL);
|
|
+ tree new_name = get_identifier (gptr_name);
|
|
+ tree new_type = build_pointer_type (newtype[0]);
|
|
+ tree new_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, new_name, new_type);
|
|
+ set_var_attributes (new_var);
|
|
+ pc_gptr = new_var;
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ fprintf (dump_file, "\nType: %s has create global header for pointer"
|
|
+ " compression: %s\n", type_name, gptr_name);
|
|
+
|
|
+ free (gptr_name);
|
|
+}
|
|
+
|
|
/* Add FIELD to the list of fields that use this type. */
|
|
|
|
void
|
|
@@ -798,15 +853,31 @@ srfield::create_new_reorder_fields (tree newtype[max_split],
|
|
fields.safe_push (field);
|
|
}
|
|
|
|
- DECL_NAME (field) = DECL_NAME (fielddecl);
|
|
if (type == NULL)
|
|
- /* Common members do not need to reconstruct.
|
|
+ {
|
|
+ DECL_NAME (field) = DECL_NAME (fielddecl);
|
|
+ /* Common members do not need to reconstruct.
|
|
Otherwise, int* -> int** or void* -> void**. */
|
|
- TREE_TYPE (field) = nt;
|
|
+ TREE_TYPE (field) = nt;
|
|
+ SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl));
|
|
+ }
|
|
+ else if (type->pc_candidate)
|
|
+ {
|
|
+ const char *old_name = IDENTIFIER_POINTER (DECL_NAME (fielddecl));
|
|
+ char *new_name = concat (old_name, "_pc", NULL);
|
|
+ DECL_NAME (field) = get_identifier (new_name);
|
|
+ free (new_name);
|
|
+ TREE_TYPE (field) = make_unsigned_type (compressed_size);
|
|
+ SET_DECL_ALIGN (field, compressed_size);
|
|
+ }
|
|
else
|
|
- TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt);
|
|
+ {
|
|
+ TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt);
|
|
+ DECL_NAME (field) = DECL_NAME (fielddecl);
|
|
+ SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl));
|
|
+ }
|
|
+
|
|
DECL_SOURCE_LOCATION (field) = DECL_SOURCE_LOCATION (fielddecl);
|
|
- SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl));
|
|
DECL_USER_ALIGN (field) = DECL_USER_ALIGN (fielddecl);
|
|
TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (fielddecl);
|
|
DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (fielddecl);
|
|
@@ -925,6 +996,10 @@ srtype::create_new_type (void)
|
|
&& has_dead_field ())
|
|
fprintf (dump_file, "Dead field elimination.\n");
|
|
}
|
|
+
|
|
+ if (pc_candidate && pc_gptr == NULL_TREE)
|
|
+ create_global_ptr_for_pc ();
|
|
+
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Created %d types:\n", maxclusters);
|
|
@@ -1338,6 +1413,30 @@ public:
|
|
|
|
unsigned execute_struct_relayout (void);
|
|
bool remove_dead_field_stmt (tree lhs);
|
|
+
|
|
+ // Pointer compression methods:
|
|
+ void check_and_prune_struct_for_pointer_compression (void);
|
|
+ void try_rewrite_with_pointer_compression (gassign *, gimple_stmt_iterator *,
|
|
+ tree, tree, tree &, tree &);
|
|
+ bool safe_void_cmp_p (tree, srtype *);
|
|
+ bool pc_candidate_st_type_p (tree);
|
|
+ bool pc_candidate_tree_p (tree);
|
|
+ bool pc_type_conversion_candidate_p (tree);
|
|
+ bool pc_direct_rewrite_chance_p (tree, tree &);
|
|
+ bool compress_candidate_with_check (gimple_stmt_iterator *, tree, tree &);
|
|
+ bool compress_candidate (gassign *, gimple_stmt_iterator *, tree, tree &);
|
|
+ bool decompress_candidate_with_check (gimple_stmt_iterator *, tree, tree &);
|
|
+ bool decompress_candidate (gimple_stmt_iterator *, tree, tree, tree &,
|
|
+ tree &);
|
|
+ srtype *get_compression_candidate_type (tree);
|
|
+ tree compress_ptr_to_offset (tree, srtype *, gimple_stmt_iterator *);
|
|
+ tree decompress_offset_to_ptr (tree, srtype *, gimple_stmt_iterator *);
|
|
+ basic_block create_bb_for_compress_candidate (basic_block, tree, srtype *,
|
|
+ tree &);
|
|
+ basic_block create_bb_for_decompress_candidate (basic_block, tree, srtype *,
|
|
+ tree &);
|
|
+ basic_block create_bb_for_compress_nullptr (basic_block, tree &);
|
|
+ basic_block create_bb_for_decompress_nullptr (basic_block, tree, tree &);
|
|
};
|
|
|
|
struct ipa_struct_relayout
|
|
@@ -1386,26 +1485,6 @@ namespace {
|
|
|
|
/* Methods for ipa_struct_relayout. */
|
|
|
|
-static void
|
|
-set_var_attributes (tree var)
|
|
-{
|
|
- if (!var)
|
|
- return;
|
|
- gcc_assert (TREE_CODE (var) == VAR_DECL);
|
|
-
|
|
- DECL_ARTIFICIAL (var) = 1;
|
|
- DECL_EXTERNAL (var) = 0;
|
|
- TREE_STATIC (var) = 1;
|
|
- TREE_PUBLIC (var) = 0;
|
|
- TREE_USED (var) = 1;
|
|
- DECL_CONTEXT (var) = NULL;
|
|
- TREE_THIS_VOLATILE (var) = 0;
|
|
- TREE_ADDRESSABLE (var) = 0;
|
|
- TREE_READONLY (var) = 0;
|
|
- if (is_global_var (var))
|
|
- set_decl_tls_model (var, TLS_MODEL_NONE);
|
|
-}
|
|
-
|
|
tree
|
|
ipa_struct_relayout::create_new_vars (tree type, const char *name)
|
|
{
|
|
@@ -2985,6 +3064,19 @@ ipa_struct_reorg::find_vars (gimple *stmt)
|
|
records the right value _1 declaration. */
|
|
find_var (gimple_assign_rhs1 (stmt), stmt);
|
|
|
|
+ /* Pointer types from non-zero pointer need to be escaped in pointer
|
|
+ compression and complete relayout.
|
|
+ e.g _1->t = (struct *) 0x400000. */
|
|
+ if (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT
|
|
+ && TREE_CODE (lhs) == COMPONENT_REF
|
|
+ && TREE_CODE (TREE_TYPE (lhs)) == POINTER_TYPE
|
|
+ && TREE_CODE (rhs) == INTEGER_CST
|
|
+ && !integer_zerop (rhs))
|
|
+ {
|
|
+ mark_type_as_escape (inner_type (TREE_TYPE (lhs)),
|
|
+ escape_cast_int, stmt);
|
|
+ }
|
|
+
|
|
/* Add a safe func mechanism. */
|
|
bool l_find = true;
|
|
bool r_find = true;
|
|
@@ -3436,12 +3528,13 @@ is_result_of_mult (tree arg, tree *num, tree struct_size)
|
|
bool
|
|
ipa_struct_reorg::handled_allocation_stmt (gimple *stmt)
|
|
{
|
|
- if ((current_layout_opt_level >= STRUCT_REORDER_FIELDS)
|
|
+ if ((current_layout_opt_level & STRUCT_REORDER_FIELDS)
|
|
&& (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)
|
|
|| gimple_call_builtin_p (stmt, BUILT_IN_MALLOC)
|
|
|| gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)))
|
|
return true;
|
|
- if ((current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT)
|
|
+ if ((current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT
|
|
+ || current_layout_opt_level & POINTER_COMPRESSION_SAFE)
|
|
&& gimple_call_builtin_p (stmt, BUILT_IN_CALLOC))
|
|
return true;
|
|
if ((current_layout_opt_level == STRUCT_SPLIT)
|
|
@@ -3563,14 +3656,19 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other,
|
|
}
|
|
}
|
|
/* x_1 = y.x_nodes; void *x;
|
|
- Directly mark the structure pointer type assigned
|
|
- to the void* variable as escape. */
|
|
+ Mark the structure pointer type assigned
|
|
+ to the void* variable as escape. Unless the void* is only used to compare
|
|
+ with variables of the same type. */
|
|
else if (current_layout_opt_level >= STRUCT_REORDER_FIELDS
|
|
&& TREE_CODE (side) == SSA_NAME
|
|
&& VOID_POINTER_P (TREE_TYPE (side))
|
|
&& SSA_NAME_VAR (side)
|
|
&& VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (side))))
|
|
- mark_type_as_escape (TREE_TYPE (other), escape_cast_void, stmt);
|
|
+ if (current_layout_opt_level < POINTER_COMPRESSION_SAFE
|
|
+ || !safe_void_cmp_p (side, type))
|
|
+ {
|
|
+ mark_type_as_escape (TREE_TYPE (other), escape_cast_void, stmt);
|
|
+ }
|
|
|
|
check_ptr_layers (side, other, stmt);
|
|
}
|
|
@@ -4181,7 +4279,7 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl,
|
|
void
|
|
ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type)
|
|
{
|
|
- if (current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT
|
|
+ if (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT
|
|
&& handled_allocation_stmt (stmt))
|
|
{
|
|
tree arg0 = gimple_call_arg (stmt, 0);
|
|
@@ -4200,6 +4298,23 @@ ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type)
|
|
type->has_alloc_array = type->has_alloc_array < 0
|
|
? type->has_alloc_array
|
|
: type->has_alloc_array + 1;
|
|
+
|
|
+ if (current_layout_opt_level & POINTER_COMPRESSION_SAFE
|
|
+ && TREE_CODE (arg0) == INTEGER_CST)
|
|
+ {
|
|
+ /* Only known size during compilation can be optimized
|
|
+ at this level. */
|
|
+ unsigned HOST_WIDE_INT max_alloc_size = 0;
|
|
+ switch (compressed_size)
|
|
+ {
|
|
+ case 8: max_alloc_size = 0xff; break; // max of uint8
|
|
+ case 16: max_alloc_size = 0xffff; break; // max of uint16
|
|
+ case 32: max_alloc_size = 0xffffffff; break; // max of uint32
|
|
+ default: gcc_unreachable (); break;
|
|
+ }
|
|
+ if (tree_to_uhwi (arg0) < max_alloc_size)
|
|
+ type->has_legal_alloc_num = true;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -4328,7 +4443,13 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec<srdecl *> &worklist)
|
|
if (current_layout_opt_level >= STRUCT_REORDER_FIELDS
|
|
&& SSA_NAME_VAR (ssa_name)
|
|
&& VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (ssa_name))))
|
|
- type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name));
|
|
+ {
|
|
+ if (current_layout_opt_level < POINTER_COMPRESSION_SAFE
|
|
+ || !safe_void_cmp_p (ssa_name, type))
|
|
+ {
|
|
+ type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name));
|
|
+ }
|
|
+ }
|
|
gimple *stmt = SSA_NAME_DEF_STMT (ssa_name);
|
|
|
|
/*
|
|
@@ -5294,6 +5415,8 @@ ipa_struct_reorg::create_new_types (void)
|
|
for (unsigned i = 0; i < types.length (); i++)
|
|
newtypes += types[i]->create_new_type ();
|
|
|
|
+ /* Some new types may not have been created at create_new_type (), so
|
|
+ recreate new type for all struct fields. */
|
|
if (current_layout_opt_level >= STRUCT_REORDER_FIELDS)
|
|
{
|
|
for (unsigned i = 0; i < types.length (); i++)
|
|
@@ -5304,9 +5427,18 @@ ipa_struct_reorg::create_new_types (void)
|
|
for (unsigned j = 0; j < fields->length (); j++)
|
|
{
|
|
tree field = (*fields)[j];
|
|
- TREE_TYPE (field)
|
|
- = reconstruct_complex_type (TREE_TYPE (field),
|
|
- types[i]->newtype[0]);
|
|
+ if (types[i]->pc_candidate)
|
|
+ {
|
|
+ TREE_TYPE (field)
|
|
+ = make_unsigned_type (compressed_size);
|
|
+ SET_DECL_ALIGN (field, compressed_size);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ TREE_TYPE (field)
|
|
+ = reconstruct_complex_type (TREE_TYPE (field),
|
|
+ types[i]->newtype[0]);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -5685,6 +5817,554 @@ ipa_struct_reorg::rewrite_expr (tree expr,
|
|
return true;
|
|
}
|
|
|
|
+/* Emit a series of gimples to compress the pointer to the index relative to
|
|
+ the global header. The basic blocks where gsi is located must have at least
|
|
+ one stmt. */
|
|
+
|
|
+tree
|
|
+ipa_struct_reorg::compress_ptr_to_offset (tree xhs, srtype *type,
|
|
+ gimple_stmt_iterator *gsi)
|
|
+{
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ fprintf (dump_file, "\nCompress candidate pointer:\n");
|
|
+ print_generic_expr (dump_file, xhs);
|
|
+ fprintf (dump_file, "\nto offset:\n");
|
|
+ }
|
|
+
|
|
+ /* Emit gimple _X1 = ptr - gptr. */
|
|
+ tree pointer_addr = fold_convert (long_unsigned_type_node, xhs);
|
|
+ tree gptr_addr = fold_convert (long_unsigned_type_node, type->pc_gptr);
|
|
+ tree step1 = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node,
|
|
+ pointer_addr, gptr_addr);
|
|
+
|
|
+ /* Emit gimple _X2 = _X1 / sizeof (struct). */
|
|
+ tree step2 = gimplify_build2 (gsi, TRUNC_DIV_EXPR, long_unsigned_type_node,
|
|
+ step1, TYPE_SIZE_UNIT (type->newtype[0]));
|
|
+
|
|
+ /* Emit gimple _X3 = _X2 + 1. */
|
|
+ tree step3 = gimplify_build2 (gsi, PLUS_EXPR, long_unsigned_type_node,
|
|
+ step2, build_one_cst (long_unsigned_type_node));
|
|
+
|
|
+ /* Emit _X4 = (compressed_size) _X3. */
|
|
+ tree step4 = gimplify_build1 (gsi, NOP_EXPR,
|
|
+ make_unsigned_type (compressed_size), step3);
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ print_generic_expr (dump_file, step3);
|
|
+ fprintf (dump_file, "\n");
|
|
+ }
|
|
+ return step4;
|
|
+}
|
|
+
|
|
+/* Emit a series of gimples to decompress the index into the original
|
|
+ pointer. The basic blocks where gsi is located must have at least
|
|
+ one stmt. */
|
|
+
|
|
+tree
|
|
+ipa_struct_reorg::decompress_offset_to_ptr (tree xhs, srtype *type,
|
|
+ gimple_stmt_iterator *gsi)
|
|
+{
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ fprintf (dump_file, "\nDecompress candidate offset:\n");
|
|
+ print_generic_expr (dump_file, xhs);
|
|
+ fprintf (dump_file, "\nto pointer:\n");
|
|
+ }
|
|
+
|
|
+ /* Emit _X1 = xhs - 1. */
|
|
+ tree offset = fold_convert (long_unsigned_type_node, xhs);
|
|
+ tree step1 = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node,
|
|
+ offset,
|
|
+ build_one_cst (long_unsigned_type_node));
|
|
+
|
|
+ /* Emit _X2 = _X1 * sizeof (struct). */
|
|
+ tree step2 = gimplify_build2 (gsi, MULT_EXPR, long_unsigned_type_node,
|
|
+ step1, TYPE_SIZE_UNIT (type->newtype[0]));
|
|
+
|
|
+ /* Emit _X3 = phead + _X2. */
|
|
+ tree gptr_addr = fold_convert (long_unsigned_type_node, type->pc_gptr);
|
|
+ tree step3 = gimplify_build2 (gsi, PLUS_EXPR, long_unsigned_type_node,
|
|
+ gptr_addr, step2);
|
|
+
|
|
+ /* Emit _X4 = (struct *) _X3. */
|
|
+ tree step4 = gimplify_build1 (gsi, NOP_EXPR, TREE_TYPE (type->pc_gptr),
|
|
+ step3);
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ print_generic_expr (dump_file, step3);
|
|
+ fprintf (dump_file, "\n");
|
|
+ }
|
|
+ return step4;
|
|
+}
|
|
+
|
|
+/* Return the compression candidate srtype of SSA_NAME or COMPONENT_REF. */
|
|
+
|
|
+srtype *
|
|
+ipa_struct_reorg::get_compression_candidate_type (tree xhs)
|
|
+{
|
|
+ if (xhs == NULL_TREE)
|
|
+ return NULL;
|
|
+
|
|
+ if (TREE_CODE (xhs) == SSA_NAME || TREE_CODE (xhs) == COMPONENT_REF)
|
|
+ {
|
|
+ srtype *access_type = find_type (inner_type (TREE_TYPE (xhs)));
|
|
+ if (access_type != NULL && access_type->pc_candidate)
|
|
+ return access_type;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/* True if the input type is the candidate type for pointer compression. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::pc_candidate_st_type_p (tree type)
|
|
+{
|
|
+ if (type == NULL_TREE)
|
|
+ return false;
|
|
+
|
|
+ if (TREE_CODE (type) == POINTER_TYPE)
|
|
+ {
|
|
+ if (TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE)
|
|
+ {
|
|
+ srtype *access_type = find_type (TREE_TYPE (type));
|
|
+ if (access_type != NULL && access_type->pc_candidate)
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* True if the input xhs is a candidate for pointer compression. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::pc_candidate_tree_p (tree xhs)
|
|
+{
|
|
+ if (xhs == NULL_TREE)
|
|
+ return false;
|
|
+
|
|
+ if (TREE_CODE (xhs) == COMPONENT_REF)
|
|
+ {
|
|
+ srtype *base_type = find_type (TREE_TYPE (TREE_OPERAND (xhs, 0)));
|
|
+ if (base_type == NULL || base_type->has_escaped ())
|
|
+ return false;
|
|
+
|
|
+ return pc_candidate_st_type_p (TREE_TYPE (xhs));
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* True if xhs is a component_ref that base has escaped but uses a compression
|
|
+ candidate type. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::pc_type_conversion_candidate_p (tree xhs)
|
|
+{
|
|
+ if (xhs == NULL_TREE)
|
|
+ return false;
|
|
+
|
|
+ if (TREE_CODE (xhs) == COMPONENT_REF)
|
|
+ {
|
|
+ srtype *base_type = find_type (TREE_TYPE (TREE_OPERAND (xhs, 0)));
|
|
+ if (base_type != NULL && base_type->has_escaped ())
|
|
+ return pc_candidate_st_type_p (TREE_TYPE (xhs));
|
|
+
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* Creates a new basic block with zero for compressed null pointers. */
|
|
+
|
|
+basic_block
|
|
+ipa_struct_reorg::create_bb_for_compress_nullptr (basic_block last_bb,
|
|
+ tree &phi)
|
|
+{
|
|
+ basic_block new_bb = create_empty_bb (last_bb);
|
|
+ if (last_bb->loop_father != NULL)
|
|
+ {
|
|
+ add_bb_to_loop (new_bb, last_bb->loop_father);
|
|
+ loops_state_set (LOOPS_NEED_FIXUP);
|
|
+ }
|
|
+
|
|
+ /* Emit phi = 0. */
|
|
+ gimple_stmt_iterator gsi = gsi_last_bb (new_bb);
|
|
+ phi = make_ssa_name (make_unsigned_type (compressed_size));
|
|
+ tree rhs = build_int_cst (make_unsigned_type (compressed_size), 0);
|
|
+ gimple *new_stmt = gimple_build_assign (phi, rhs);
|
|
+ gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT);
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ fprintf (dump_file, "\nCreate bb %d for compress nullptr:\n",
|
|
+ new_bb->index);
|
|
+ gimple_dump_bb (dump_file, new_bb, 0, dump_flags);
|
|
+ }
|
|
+ return new_bb;
|
|
+}
|
|
+
|
|
+/* Create a new basic block to compress the pointer to the index relative to
|
|
+ the allocated memory pool header. */
|
|
+
|
|
+basic_block
|
|
+ipa_struct_reorg::create_bb_for_compress_candidate (basic_block last_bb,
|
|
+ tree new_rhs, srtype *type,
|
|
+ tree &phi)
|
|
+{
|
|
+ basic_block new_bb = create_empty_bb (last_bb);
|
|
+ if (last_bb->loop_father != NULL)
|
|
+ {
|
|
+ add_bb_to_loop (new_bb, last_bb->loop_father);
|
|
+ loops_state_set (LOOPS_NEED_FIXUP);
|
|
+ }
|
|
+
|
|
+ gimple_stmt_iterator gsi = gsi_last_bb (new_bb);
|
|
+ /* compress_ptr_to_offset () needs at least one stmt in target bb. */
|
|
+ gsi_insert_after (&gsi, gimple_build_nop (), GSI_NEW_STMT);
|
|
+ phi = compress_ptr_to_offset (new_rhs, type, &gsi);
|
|
+ /* Remove the NOP created above. */
|
|
+ gsi_remove (&gsi, true);
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ fprintf (dump_file, "\nCreate bb %d for compress candidate:\n",
|
|
+ new_bb->index);
|
|
+ gimple_dump_bb (dump_file, new_bb, 0, dump_flags);
|
|
+ }
|
|
+ return new_bb;
|
|
+}
|
|
+
|
|
+/* Compression can be simplified by these following cases:
|
|
+ 1. if rhs is NULL, uses zero to represent it.
|
|
+ 2. if new_rhs has been converted into INTEGER_TYPE in the previous stmt,
|
|
+ just use it here. For example:
|
|
+ _1 = t->s
|
|
+ -> tt->s = _1. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::pc_direct_rewrite_chance_p (tree rhs, tree &new_rhs)
|
|
+{
|
|
+ if (integer_zerop (rhs))
|
|
+ {
|
|
+ new_rhs = build_int_cst (make_unsigned_type (compressed_size), 0);
|
|
+ return true;
|
|
+ }
|
|
+ else if (new_rhs && TREE_CODE (TREE_TYPE (new_rhs)) == INTEGER_TYPE)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* Perform pointer compression with check. The conversion will be as shown in
|
|
+ the following example:
|
|
+ Orig bb:
|
|
+ bb <1>:
|
|
+ _1->t = _2
|
|
+
|
|
+ will be transformed to:
|
|
+ bb <1>:
|
|
+ _3 = _2
|
|
+ if (_2 == NULL)
|
|
+ goto bb <2>
|
|
+ else
|
|
+ goto bb <3>
|
|
+
|
|
+ bb <2>:
|
|
+ _3 = 0
|
|
+ goto bb <4>
|
|
+
|
|
+ bb <3>:
|
|
+ ...
|
|
+ _4 = compress (_2)
|
|
+ goto bb <4>
|
|
+
|
|
+ bb <4>:
|
|
+ _5 = PHI (_3, _4)
|
|
+ _1->t = _5
|
|
+ The gsi will move to the beginning of split dst bb <4>, _1->t = _5 will be
|
|
+ emitted by rewrite_assign (). */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::compress_candidate_with_check (gimple_stmt_iterator *gsi,
|
|
+ tree rhs, tree &new_rhs)
|
|
+{
|
|
+ tree cond_lhs = make_ssa_name (TREE_TYPE (new_rhs));
|
|
+ gimple *assign_stmt = gimple_build_assign (cond_lhs, new_rhs);
|
|
+ gsi_insert_before (gsi, assign_stmt, GSI_SAME_STMT);
|
|
+
|
|
+ /* Insert cond stmt. */
|
|
+ tree rhs_pointer_type = build_pointer_type (TREE_TYPE (new_rhs));
|
|
+ gcond *cond = gimple_build_cond (EQ_EXPR, cond_lhs,
|
|
+ build_int_cst (rhs_pointer_type, 0),
|
|
+ NULL_TREE, NULL_TREE);
|
|
+ gimple_set_location (cond, UNKNOWN_LOCATION);
|
|
+ gsi_insert_before (gsi, cond, GSI_SAME_STMT);
|
|
+
|
|
+ edge e = split_block (cond->bb, cond);
|
|
+ basic_block split_src_bb = e->src;
|
|
+ basic_block split_dst_bb = e->dest;
|
|
+
|
|
+ /* Create bb for nullptr. */
|
|
+ tree phi1 = NULL_TREE;
|
|
+ basic_block true_bb = create_bb_for_compress_nullptr (split_src_bb, phi1);
|
|
+
|
|
+ /* Create bb for comprssion. */
|
|
+ srtype *type = get_compression_candidate_type (rhs);
|
|
+ gcc_assert (type != NULL);
|
|
+ tree phi2 = NULL_TREE;
|
|
+ basic_block false_bb = create_bb_for_compress_candidate (true_bb, new_rhs,
|
|
+ type, phi2);
|
|
+
|
|
+ /* Rebuild and reset cfg. */
|
|
+ remove_edge_raw (e);
|
|
+
|
|
+ edge etrue = make_edge (split_src_bb, true_bb, EDGE_TRUE_VALUE);
|
|
+ etrue->probability = profile_probability::unlikely ();
|
|
+ true_bb->count = etrue->count ();
|
|
+
|
|
+ edge efalse = make_edge (split_src_bb, false_bb, EDGE_FALSE_VALUE);
|
|
+ efalse->probability = profile_probability::likely ();
|
|
+ false_bb->count = efalse->count ();
|
|
+
|
|
+ edge e1 = make_single_succ_edge (true_bb, split_dst_bb, EDGE_FALLTHRU);
|
|
+ edge e2 = make_single_succ_edge (false_bb, split_dst_bb, EDGE_FALLTHRU);
|
|
+
|
|
+ tree phi = make_ssa_name (make_unsigned_type (compressed_size));
|
|
+ gphi *phi_node = create_phi_node (phi, split_dst_bb);
|
|
+ add_phi_arg (phi_node, phi1, e1, UNKNOWN_LOCATION);
|
|
+ add_phi_arg (phi_node, phi2, e2, UNKNOWN_LOCATION);
|
|
+
|
|
+ if (dom_info_available_p (CDI_DOMINATORS))
|
|
+ {
|
|
+ set_immediate_dominator (CDI_DOMINATORS, split_dst_bb, split_src_bb);
|
|
+ set_immediate_dominator (CDI_DOMINATORS, true_bb, split_src_bb);
|
|
+ set_immediate_dominator (CDI_DOMINATORS, false_bb, split_src_bb);
|
|
+ }
|
|
+ *gsi = gsi_start_bb (split_dst_bb);
|
|
+ new_rhs = phi;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/* If there is a direct rewrite chance or simplification opportunity, perform
|
|
+ the simplified compression rewrite. Otherwise, create a cond expression and
|
|
+ two basic blocks to implement pointer compression. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::compress_candidate (gassign *stmt, gimple_stmt_iterator *gsi,
|
|
+ tree rhs, tree &new_rhs)
|
|
+{
|
|
+ if (pc_direct_rewrite_chance_p (rhs, new_rhs))
|
|
+ return true;
|
|
+
|
|
+ return compress_candidate_with_check (gsi, rhs, new_rhs);
|
|
+}
|
|
+
|
|
+/* Create a new basic block to decompress the index to null pointer. */
|
|
+
|
|
+basic_block
|
|
+ipa_struct_reorg::create_bb_for_decompress_nullptr (basic_block last_bb,
|
|
+ tree new_rhs,
|
|
+ tree &phi_node)
|
|
+{
|
|
+ basic_block new_bb = create_empty_bb (last_bb);
|
|
+ if (last_bb->loop_father != NULL)
|
|
+ {
|
|
+ add_bb_to_loop (new_bb, last_bb->loop_father);
|
|
+ loops_state_set (LOOPS_NEED_FIXUP);
|
|
+ }
|
|
+ gimple_stmt_iterator gsi = gsi_last_bb (new_bb);
|
|
+ tree rhs_pointer_type = build_pointer_type (TREE_TYPE (new_rhs));
|
|
+ phi_node = make_ssa_name (rhs_pointer_type);
|
|
+ gimple *new_stmt = gimple_build_assign (phi_node,
|
|
+ build_int_cst (rhs_pointer_type, 0));
|
|
+ gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT);
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ fprintf (dump_file, "\nCreate bb %d for decompress nullptr:\n",
|
|
+ new_bb->index);
|
|
+ gimple_dump_bb (dump_file, new_bb, 0, dump_flags);
|
|
+ }
|
|
+ return new_bb;
|
|
+}
|
|
+
|
|
+/* Create a new basic block to decompress the index into original pointer. */
|
|
+
|
|
+basic_block
|
|
+ipa_struct_reorg::create_bb_for_decompress_candidate (basic_block last_bb,
|
|
+ tree lhs, srtype *type,
|
|
+ tree &phi_node)
|
|
+{
|
|
+ basic_block new_bb = create_empty_bb (last_bb);
|
|
+ if (last_bb->loop_father != NULL)
|
|
+ {
|
|
+ add_bb_to_loop (new_bb, last_bb->loop_father);
|
|
+ loops_state_set (LOOPS_NEED_FIXUP);
|
|
+ }
|
|
+ gimple_stmt_iterator gsi = gsi_last_bb (new_bb);
|
|
+ /* decompress_ptr_to_offset () needs at least one stmt in target bb. */
|
|
+ gsi_insert_after (&gsi, gimple_build_nop (), GSI_NEW_STMT);
|
|
+ phi_node = decompress_offset_to_ptr (lhs, type, &gsi);
|
|
+ /* Remove the NOP created above. */
|
|
+ gsi_remove (&gsi, true);
|
|
+
|
|
+ if (dump_file && (dump_flags & TDF_DETAILS))
|
|
+ {
|
|
+ fprintf (dump_file, "\nCreate bb %d for decompress candidate:\n",
|
|
+ new_bb->index);
|
|
+ gimple_dump_bb (dump_file, new_bb, 0, dump_flags);
|
|
+ }
|
|
+ return new_bb;
|
|
+}
|
|
+
|
|
+/* Perform pointer decompression with check. The conversion will be as shown
|
|
+ in the following example:
|
|
+ Orig bb:
|
|
+ bb <1>:
|
|
+ _1 = _2->t
|
|
+
|
|
+ will be transformed to:
|
|
+ bb <1>:
|
|
+ _3 = _2->t
|
|
+ if (_3 == 0)
|
|
+ goto bb <2>
|
|
+ else
|
|
+ goto bb <3>
|
|
+
|
|
+ bb <2>:
|
|
+ _4 = NULL
|
|
+ goto bb <4>
|
|
+
|
|
+ bb <3>:
|
|
+ ...
|
|
+ _5 = decompress (_3)
|
|
+ goto bb <4>
|
|
+
|
|
+ bb <4>:
|
|
+ _6 = PHI (_4, _5)
|
|
+ _1 = _6
|
|
+ The gsi will move to the beginning of split dst bb <4>, _1 = _6 will be
|
|
+ emitted by rewrite_assign (). */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::decompress_candidate_with_check (gimple_stmt_iterator *gsi,
|
|
+ tree rhs, tree &new_rhs)
|
|
+{
|
|
+ /* Insert cond stmt. */
|
|
+ tree cond_lhs = make_ssa_name (TREE_TYPE (new_rhs));
|
|
+ gassign *cond_assign = gimple_build_assign (cond_lhs, new_rhs);
|
|
+ gsi_insert_before (gsi, cond_assign, GSI_SAME_STMT);
|
|
+
|
|
+ tree pc_type = make_unsigned_type (compressed_size);
|
|
+ gcond *cond = gimple_build_cond (EQ_EXPR, cond_lhs,
|
|
+ build_int_cst (pc_type, 0),
|
|
+ NULL_TREE, NULL_TREE);
|
|
+ gimple_set_location (cond, UNKNOWN_LOCATION);
|
|
+ gsi_insert_before (gsi, cond, GSI_SAME_STMT);
|
|
+
|
|
+ /* Split bb. */
|
|
+ edge e = split_block (cond->bb, cond);
|
|
+ basic_block split_src_bb = e->src;
|
|
+ basic_block split_dst_bb = e->dest;
|
|
+
|
|
+ /* Create bb for decompress nullptr. */
|
|
+ tree phi1 = NULL_TREE;
|
|
+ basic_block true_bb = create_bb_for_decompress_nullptr (split_src_bb,
|
|
+ new_rhs, phi1);
|
|
+
|
|
+ /* Create bb for decomprssion candidate. */
|
|
+ tree phi2 = NULL_TREE;
|
|
+ srtype *type = get_compression_candidate_type (rhs);
|
|
+ gcc_assert (type != NULL);
|
|
+ basic_block false_bb = create_bb_for_decompress_candidate (true_bb, cond_lhs,
|
|
+ type, phi2);
|
|
+
|
|
+ /* Refresh and reset cfg. */
|
|
+ remove_edge_raw (e);
|
|
+
|
|
+ edge etrue = make_edge (split_src_bb, true_bb, EDGE_TRUE_VALUE);
|
|
+ etrue->probability = profile_probability::unlikely ();
|
|
+ true_bb->count = etrue->count ();
|
|
+
|
|
+ edge efalse = make_edge (split_src_bb, false_bb, EDGE_FALSE_VALUE);
|
|
+ efalse->probability = profile_probability::likely ();
|
|
+ false_bb->count = efalse->count ();
|
|
+
|
|
+ edge e1 = make_single_succ_edge (true_bb, split_dst_bb, EDGE_FALLTHRU);
|
|
+ edge e2 = make_single_succ_edge (false_bb, split_dst_bb, EDGE_FALLTHRU);
|
|
+
|
|
+ tree phi = make_ssa_name (build_pointer_type (TREE_TYPE (cond_lhs)));
|
|
+ gphi *phi_node = create_phi_node (phi, split_dst_bb);
|
|
+ add_phi_arg (phi_node, phi1, e1, UNKNOWN_LOCATION);
|
|
+ add_phi_arg (phi_node, phi2, e2, UNKNOWN_LOCATION);
|
|
+
|
|
+ if (dom_info_available_p (CDI_DOMINATORS))
|
|
+ {
|
|
+ set_immediate_dominator (CDI_DOMINATORS, split_dst_bb, split_src_bb);
|
|
+ set_immediate_dominator (CDI_DOMINATORS, true_bb, split_src_bb);
|
|
+ set_immediate_dominator (CDI_DOMINATORS, false_bb, split_src_bb);
|
|
+ }
|
|
+ *gsi = gsi_start_bb (split_dst_bb);
|
|
+ new_rhs = phi;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/* If there is a simplification opportunity, perform the simplified
|
|
+ decompression rewrite. Otherwise, create a cond expression and two basic
|
|
+ blocks to implement pointer decompression. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::decompress_candidate (gimple_stmt_iterator *gsi,
|
|
+ tree lhs, tree rhs, tree &new_lhs,
|
|
+ tree &new_rhs)
|
|
+{
|
|
+ // TODO: simplifiy check and rewrite will be pushed in next PR.
|
|
+ return decompress_candidate_with_check (gsi, rhs, new_rhs);
|
|
+}
|
|
+
|
|
+/* Try to perform pointer compression and decompression. */
|
|
+
|
|
+void
|
|
+ipa_struct_reorg::try_rewrite_with_pointer_compression (gassign *stmt,
|
|
+ gimple_stmt_iterator
|
|
+ *gsi, tree lhs,
|
|
+ tree rhs, tree &new_lhs,
|
|
+ tree &new_rhs)
|
|
+{
|
|
+ bool l = pc_candidate_tree_p (lhs);
|
|
+ bool r = pc_candidate_tree_p (rhs);
|
|
+ if (!l && !r)
|
|
+ {
|
|
+ tree tmp_rhs = new_rhs == NULL_TREE ? rhs : new_rhs;
|
|
+ if (pc_type_conversion_candidate_p (lhs))
|
|
+ {
|
|
+ /* Transfer MEM[(struct *)_1].files = _4;
|
|
+ to MEM[(struct *)_1].files = (struct *)_4; */
|
|
+ new_rhs = fold_convert (TREE_TYPE (lhs), tmp_rhs);
|
|
+ }
|
|
+ else if (pc_type_conversion_candidate_p (rhs))
|
|
+ {
|
|
+ /* Transfer _4 = MEM[(struct *)_1].nodes;
|
|
+ to _4 = (new_struct *) MEM[(struct *)_1].nodes; */
|
|
+ new_rhs = fold_convert (TREE_TYPE (new_lhs), tmp_rhs);
|
|
+ }
|
|
+ }
|
|
+ else if (l && r)
|
|
+ gcc_unreachable ();
|
|
+ else if (l)
|
|
+ {
|
|
+ if (!compress_candidate (stmt, gsi, rhs, new_rhs))
|
|
+ gcc_unreachable ();
|
|
+ }
|
|
+ else if (r)
|
|
+ {
|
|
+ if (!decompress_candidate (gsi, lhs, rhs, new_lhs, new_rhs))
|
|
+ gcc_unreachable ();
|
|
+ }
|
|
+}
|
|
+
|
|
bool
|
|
ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi)
|
|
{
|
|
@@ -5880,6 +6560,9 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi)
|
|
fprintf (dump_file, "\nreplaced with:\n");
|
|
for (unsigned i = 0; i < max_split && (newlhs[i] || newrhs[i]); i++)
|
|
{
|
|
+ if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE)
|
|
+ try_rewrite_with_pointer_compression (stmt, gsi, lhs, rhs,
|
|
+ newlhs[i], newrhs[i]);
|
|
gimple *newstmt = gimple_build_assign (newlhs[i] ? newlhs[i] : lhs,
|
|
newrhs[i] ? newrhs[i] : rhs);
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
@@ -5956,6 +6639,13 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi)
|
|
gcc_assert (false);
|
|
gimple_call_set_lhs (g, decl->newdecl[i]);
|
|
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
|
+ if (type->pc_candidate)
|
|
+ {
|
|
+ /* Init global header for pointer compression. */
|
|
+ gassign *gptr
|
|
+ = gimple_build_assign (type->pc_gptr, decl->newdecl[i]);
|
|
+ gsi_insert_before (gsi, gptr, GSI_SAME_STMT);
|
|
+ }
|
|
}
|
|
return true;
|
|
}
|
|
@@ -6411,6 +7101,12 @@ ipa_struct_reorg::rewrite_functions (void)
|
|
push_cfun (DECL_STRUCT_FUNCTION (node->decl));
|
|
current_function = f;
|
|
|
|
+ if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE)
|
|
+ {
|
|
+ calculate_dominance_info (CDI_DOMINATORS);
|
|
+ loop_optimizer_init (0);
|
|
+ }
|
|
+
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "\nBefore rewrite: %dth_%s\n",
|
|
@@ -6486,6 +7182,9 @@ ipa_struct_reorg::rewrite_functions (void)
|
|
|
|
free_dominance_info (CDI_DOMINATORS);
|
|
|
|
+ if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE)
|
|
+ loop_optimizer_finalize ();
|
|
+
|
|
if (dump_file)
|
|
{
|
|
fprintf (dump_file, "\nAfter rewrite: %dth_%s\n",
|
|
@@ -6514,6 +7213,8 @@ ipa_struct_reorg::execute_struct_relayout (void)
|
|
continue;
|
|
if (types[i]->chain_type)
|
|
continue;
|
|
+ if (get_type_name (types[i]->type) == NULL)
|
|
+ continue;
|
|
retval |= ipa_struct_relayout (type, this).execute ();
|
|
}
|
|
|
|
@@ -6530,6 +7231,131 @@ ipa_struct_reorg::execute_struct_relayout (void)
|
|
return retval;
|
|
}
|
|
|
|
+/* True if the var with void type is only used to compare with the same
|
|
+ target type. */
|
|
+
|
|
+bool
|
|
+ipa_struct_reorg::safe_void_cmp_p (tree var, srtype *type)
|
|
+{
|
|
+ imm_use_iterator imm_iter;
|
|
+ use_operand_p use_p;
|
|
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
|
|
+ {
|
|
+ gimple *use_stmt = USE_STMT (use_p);
|
|
+ if (is_gimple_debug (use_stmt))
|
|
+ continue;
|
|
+
|
|
+ if (gimple_code (use_stmt) == GIMPLE_COND)
|
|
+ {
|
|
+ tree lhs = gimple_cond_lhs (use_stmt);
|
|
+ tree rhs = gimple_cond_rhs (use_stmt);
|
|
+ tree xhs = lhs == var ? rhs : lhs;
|
|
+ if (types_compatible_p (inner_type (TREE_TYPE (xhs)), type->type))
|
|
+ continue;
|
|
+
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/* Mark the structure that should perform pointer compression. */
|
|
+
|
|
+void
|
|
+ipa_struct_reorg::check_and_prune_struct_for_pointer_compression (void)
|
|
+{
|
|
+ unsigned pc_transform_num = 0;
|
|
+
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, "\nMark the structure that should perform pointer"
|
|
+ " compression:\n");
|
|
+
|
|
+ for (unsigned i = 0; i < types.length (); i++)
|
|
+ {
|
|
+ srtype *type = types[i];
|
|
+ if (dump_file)
|
|
+ print_generic_expr (dump_file, type->type);
|
|
+
|
|
+ if (type->has_escaped ())
|
|
+ {
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " has escaped by %s, skip compression.\n",
|
|
+ type->escape_reason ());
|
|
+ continue;
|
|
+ }
|
|
+ if (TYPE_FIELDS (type->type) == NULL)
|
|
+ {
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " has zero field, skip compression.\n");
|
|
+ continue;
|
|
+ }
|
|
+ if (type->chain_type)
|
|
+ {
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " is chain_type, skip compression.\n");
|
|
+ continue;
|
|
+ }
|
|
+ if (type->has_alloc_array != 1)
|
|
+ {
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " has alloc number: %d, skip compression.\n",
|
|
+ type->has_alloc_array);
|
|
+ continue;
|
|
+ }
|
|
+ if (get_type_name (type->type) == NULL)
|
|
+ {
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " has empty struct name,"
|
|
+ " skip compression.\n");
|
|
+ continue;
|
|
+ }
|
|
+ if ((current_layout_opt_level & POINTER_COMPRESSION_SAFE)
|
|
+ && !type->has_legal_alloc_num)
|
|
+ {
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " has illegal struct array size,"
|
|
+ " skip compression.\n");
|
|
+ continue;
|
|
+ }
|
|
+ pc_transform_num++;
|
|
+ type->pc_candidate = true;
|
|
+ if (dump_file)
|
|
+ fprintf (dump_file, " attemps to do pointer compression.\n");
|
|
+ }
|
|
+
|
|
+ if (dump_file)
|
|
+ {
|
|
+ if (pc_transform_num)
|
|
+ fprintf (dump_file, "\nNumber of structures to transform in "
|
|
+ "pointer compression is %d\n", pc_transform_num);
|
|
+ else
|
|
+ fprintf (dump_file, "\nNo structures to transform in "
|
|
+ "pointer compression.\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Init pointer size from parameter param_pointer_compression_size. */
|
|
+
|
|
+static void
|
|
+init_pointer_size_for_pointer_compression (void)
|
|
+{
|
|
+ switch (param_pointer_compression_size)
|
|
+ {
|
|
+ case 8:
|
|
+ compressed_size = 8; // sizeof (uint8)
|
|
+ break;
|
|
+ case 16:
|
|
+ compressed_size = 16; // sizeof (uint16)
|
|
+ break;
|
|
+ case 32:
|
|
+ compressed_size = 32; // sizeof (uint32)
|
|
+ break;
|
|
+ default:
|
|
+ error ("Invalid pointer compression size, using the following param: "
|
|
+ "\"--param compressed-pointer-size=[8,16,32]\"");
|
|
+ }
|
|
+}
|
|
+
|
|
unsigned int
|
|
ipa_struct_reorg::execute (unsigned int opt)
|
|
{
|
|
@@ -6551,6 +7377,8 @@ ipa_struct_reorg::execute (unsigned int opt)
|
|
if (current_layout_opt_level == STRUCT_SPLIT)
|
|
analyze_types ();
|
|
|
|
+ if (opt >= POINTER_COMPRESSION_SAFE)
|
|
+ check_and_prune_struct_for_pointer_compression ();
|
|
ret = rewrite_functions ();
|
|
}
|
|
else
|
|
@@ -6598,6 +7426,8 @@ public:
|
|
unsigned int level = 0;
|
|
switch (struct_layout_optimize_level)
|
|
{
|
|
+ case 4: level |= POINTER_COMPRESSION_SAFE;
|
|
+ // FALLTHRU
|
|
case 3: level |= DEAD_FIELD_ELIMINATION;
|
|
// FALLTHRU
|
|
case 2: level |= STRUCT_REORDER_FIELDS;
|
|
@@ -6609,6 +7439,10 @@ public:
|
|
case 0: break;
|
|
default: gcc_unreachable ();
|
|
}
|
|
+
|
|
+ if (level & POINTER_COMPRESSION_SAFE)
|
|
+ init_pointer_size_for_pointer_compression ();
|
|
+
|
|
/* Preserved for backward compatibility, reorder fields needs run before
|
|
struct split and complete struct relayout. */
|
|
if (flag_ipa_reorder_fields && level < STRUCT_REORDER_FIELDS)
|
|
diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h
|
|
index 719f7b308..6c4469597 100644
|
|
--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h
|
|
+++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h
|
|
@@ -121,7 +121,10 @@ private:
|
|
|
|
public:
|
|
tree newtype[max_split];
|
|
+ tree pc_gptr;
|
|
bool visited;
|
|
+ bool pc_candidate;
|
|
+ bool has_legal_alloc_num;
|
|
/* Negative number means it has illegal allocated arrays
|
|
that we do not optimize. */
|
|
int has_alloc_array;
|
|
@@ -145,6 +148,7 @@ public:
|
|
void analyze (void);
|
|
bool has_dead_field (void);
|
|
void mark_escape (escape_type, gimple *stmt);
|
|
+ void create_global_ptr_for_pc ();
|
|
bool has_escaped (void)
|
|
{
|
|
return escapes != does_not_escape;
|
|
diff --git a/gcc/params.opt b/gcc/params.opt
|
|
index 1ddf1343f..d2196dc68 100644
|
|
--- a/gcc/params.opt
|
|
+++ b/gcc/params.opt
|
|
@@ -1205,4 +1205,8 @@ Enum(vrp_mode) String(vrp) Value(VRP_MODE_VRP)
|
|
EnumValue
|
|
Enum(vrp_mode) String(ranger) Value(VRP_MODE_RANGER)
|
|
|
|
+-param=compressed-pointer-size=
|
|
+Common Joined UInteger Var(param_pointer_compression_size) Init(32) IntegerRange(8, 32) Param Optimization
|
|
+Target size of compressed pointer, which should be 8, 16 or 32.
|
|
+
|
|
; This comment is to ensure we retain the blank line above.
|
|
--
|
|
2.33.0
|
|
|