Projects
openEuler:24.03:SP1:Everything
gcc
_service:tar_scm:0029-Struct-Reorg-Add-Safe-Str...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:0029-Struct-Reorg-Add-Safe-Structure-Pointer-Compression.patch of Package gcc
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
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2