diff --git a/gcc/common.opt b/gcc/common.opt index b5ea3c7a12793e4f6e866a7f90b0f140cb84a797..6278ebb39e135ab36e749642f8ee7786d16e829d 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1884,8 +1884,8 @@ Common Report 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, pointer-compression optimizations. fipa-extend-auto-profile Common Report Var(flag_ipa_extend_auto_profile) diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.c b/gcc/ipa-struct-reorg/ipa-struct-reorg.c index 00dc4bf1d25964ba93c851d819ec0939b752ef17..2b799339ad61630952727c2fd21b04219117ee35 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.c +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.c @@ -83,6 +83,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" #include "ssa.h" #include "tree-dfa.h" #include "fold-const.h" @@ -109,6 +110,30 @@ namespace { using namespace struct_reorg; using namespace 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); + } +} + /* Return true iff TYPE is stdarg va_list type. */ static inline bool @@ -247,9 +272,21 @@ enum struct_layout_opt_level NONE = 0, STRUCT_REORG, STRUCT_REORDER_FIELDS, - DEAD_FIELD_ELIMINATION + DEAD_FIELD_ELIMINATION, + POINTER_COMPRESSION }; +/* Defines the target pointer size for pointer compression, which should be 8, + 16, 32. */ + +int POINTER_COMPRESSION_SIZE = 32; + +/* Defines null pointers of different compressed sizes. It is the maximum + number of unsigned integers of each size, which is 0xff, 0xffff, + 0xffffffff. */ + +unsigned long long POINTER_COMPRESSION_NULL_VAL = 0xffffffff; + static bool is_result_of_mult (tree arg, tree *num, tree struct_size); bool isptrptr (tree type); void get_base (tree &base, tree expr); @@ -371,7 +408,9 @@ srtype::srtype (tree type) : type (type), chain_type (false), escapes (does_not_escape), + pc_gptr (NULL), visited (false), + pc_candidate (false), has_alloc_array (0) { for (int i = 0; i < max_split; i++) @@ -452,6 +491,33 @@ srtype::mark_escape (escape_type e, gimple *stmt) } } +/* Create a global header for pointer compression. */ + +void +srtype::create_global_ptr_for_pointer_compression () +{ + if (!pc_candidate || pc_gptr != NULL) + { + return; + } + const char *type_name = get_type_name (type); + gcc_assert (type_name != NULL); + + char *gptr_name = concat (type_name, "_pchead", 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 @@ -794,21 +860,39 @@ srfield::create_new_optimized_fields (tree newtype[max_split], = fields_to_finish.get_or_insert (inner_type (type->type)); fields.safe_push (field); } - - DECL_NAME (field) = DECL_NAME (fielddecl); + if (type != NULL && type->pc_candidate) + { + const char *old_name = IDENTIFIER_POINTER (DECL_NAME (fielddecl)); + char *new_name = concat (old_name, "_ptrcmps", NULL); + DECL_NAME (field) = get_identifier (new_name); + free (new_name); + } + else + { + DECL_NAME (field) = DECL_NAME (fielddecl); + } if (type == NULL) { /* Common members do not need to reconstruct. Otherwise, int* -> int** or void* -> void**. */ TREE_TYPE (field) = nt; + SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); } else { - TREE_TYPE (field) + if (type->pc_candidate) + { + TREE_TYPE (field) = make_unsigned_type (POINTER_COMPRESSION_SIZE); + SET_DECL_ALIGN (field, POINTER_COMPRESSION_SIZE); + } + else + { + TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt); + 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); @@ -931,6 +1015,12 @@ srtype::create_new_type (void) && has_dead_field ()) fprintf (dump_file, "Dead field elimination.\n"); } + + if (pc_candidate && pc_gptr == NULL) + { + create_global_ptr_for_pointer_compression (); + } + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Created %d types:\n", maxclusters); @@ -1350,6 +1440,34 @@ public: void maybe_mark_or_record_other_side (tree side, tree other, gimple *stmt); 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 is_safe_void_cmp (tree, srtype *); + bool is_compression_candidate_type (tree); + bool is_compression_candidate (tree); + bool is_type_conversion_candidate_for_pc (tree); + bool is_direct_rewrite_chance_for_compression (tree, tree &); + bool is_simplify_chance_for_compress_candidate (gassign *, tree); + bool compress_candidate_without_check (gimple_stmt_iterator *, tree, tree &); + bool compress_candidate_with_check (gimple_stmt_iterator *, tree, tree &); + bool compress_candidate (gassign *, gimple_stmt_iterator *, tree, tree &); + bool decompress_candidate_without_check (gimple_stmt_iterator *, + tree, tree, 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 @@ -1400,30 +1518,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) { @@ -3146,6 +3240,21 @@ ipa_struct_reorg::find_vars (gimple *stmt) records the right value _1 declaration. */ find_var (gimple_assign_rhs1 (stmt), stmt); + /* Handle _1->t = (struct *) 0x123456; + Pointer types from non-zero pointer need to be escaped in pointer + compression and complete relayout. */ + if (((current_mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION) + || current_mode == 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; @@ -3730,15 +3839,20 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, gimple } } /* 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_mode == STRUCT_LAYOUT_OPTIMIZE && 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 (struct_layout_optimize_level < POINTER_COMPRESSION + || !is_safe_void_cmp (side, type)) + { + mark_type_as_escape (TREE_TYPE (other), escape_cast_void, stmt); + } } check_ptr_layers (side, other, stmt); @@ -4361,7 +4475,9 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl, void ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) { - if (current_mode == COMPLETE_STRUCT_RELAYOUT + if ((current_mode == COMPLETE_STRUCT_RELAYOUT + || (current_mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION)) && handled_allocation_stmt (stmt)) { tree arg0 = gimple_call_arg (stmt, 0); @@ -4529,7 +4645,11 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) if (current_mode == STRUCT_LAYOUT_OPTIMIZE && 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 (struct_layout_optimize_level < POINTER_COMPRESSION + || !is_safe_void_cmp (ssa_name, type)) + { + type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); + } } gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); @@ -5521,6 +5641,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_mode == STRUCT_LAYOUT_OPTIMIZE) { for (unsigned i = 0; i < types.length (); i++) @@ -5531,9 +5653,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 (POINTER_COMPRESSION_SIZE); + SET_DECL_ALIGN (field, POINTER_COMPRESSION_SIZE); + } + else + { + TREE_TYPE (field) + = reconstruct_complex_type (TREE_TYPE (field), + types[i]->newtype[0]); + } } } } @@ -5904,6 +6035,703 @@ ipa_struct_reorg::rewrite_expr (tree expr, tree newexpr[max_split], bool ignore_ 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 _X3 = (TARGET_SIZE) _X2. */ + tree step3 = gimplify_build1 (gsi, NOP_EXPR, + make_unsigned_type (POINTER_COMPRESSION_SIZE), + step2); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_generic_expr (dump_file, step3); + fprintf (dump_file, "\n"); + } + return step3; +} + +/* 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 = offset * sizeof (struct). */ + tree offset = fold_convert (long_unsigned_type_node, xhs); + tree step1 = gimplify_build2 (gsi, MULT_EXPR, long_unsigned_type_node, + offset, TYPE_SIZE_UNIT (type->newtype[0])); + + /* Emit _X2 = phead + _X1. */ + tree gptr_addr = fold_convert (long_unsigned_type_node, type->pc_gptr); + tree step2 = gimplify_build2 (gsi, PLUS_EXPR, long_unsigned_type_node, + gptr_addr, step1); + + /* Emit _X3 = (struct *) _X2. */ + tree step3 = gimplify_build1 (gsi, NOP_EXPR, TREE_TYPE (type->pc_gptr), + step2); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_generic_expr (dump_file, step3); + fprintf (dump_file, "\n"); + } + return step3; +} + +/* Return the compression candidate srtype of SSA_NAME or COMPONENT_REF. */ + +srtype * +ipa_struct_reorg::get_compression_candidate_type (tree xhs) +{ + if (xhs == NULL) + { + 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::is_compression_candidate_type (tree type) +{ + if (type == NULL) + { + 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::is_compression_candidate (tree xhs) +{ + if (xhs == NULL) + { + 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 is_compression_candidate_type (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::is_type_conversion_candidate_for_pc (tree xhs) +{ + if (xhs == NULL) + { + 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 is_compression_candidate_type (TREE_TYPE (xhs)); + } + } + return false; +} + +/* Create a new basic block that uses magic numbers to represent 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 _X->t_ptrcmps = NULL_MAX_VALUE. */ + gimple_stmt_iterator gsi = gsi_last_bb (new_bb); + phi = make_ssa_name (make_unsigned_type (POINTER_COMPRESSION_SIZE)); + tree rhs = build_int_cst (make_unsigned_type (POINTER_COMPRESSION_SIZE), + POINTER_COMPRESSION_NULL_VAL); + 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, use a magic fake zero 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::is_direct_rewrite_chance_for_compression (tree rhs, + tree &new_rhs) +{ + if (integer_zerop (rhs)) + { + new_rhs + = build_int_cst (make_unsigned_type (POINTER_COMPRESSION_SIZE), + POINTER_COMPRESSION_NULL_VAL); + return true; + } + else if (new_rhs && TREE_CODE (TREE_TYPE (new_rhs)) == INTEGER_TYPE) + { + return true; + } + return false; +} + +/* The following cases can simplify the checking of null pointer: + 1. rhs used as COMPONENT_REF in this basic block. + 2. rhs defined from POINTER_PLUS_EXPR. */ + +bool +ipa_struct_reorg::is_simplify_chance_for_compress_candidate (gassign *stmt, + tree rhs) +{ + imm_use_iterator imm_iter; + use_operand_p use_p; + gimple *def_stmt = SSA_NAME_DEF_STMT (rhs); + + if (def_stmt && is_gimple_assign (def_stmt) + && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR) + { + return true; + } + + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, rhs) + { + gimple *use_stmt = USE_STMT (use_p); + if (use_stmt->bb != stmt->bb || !is_gimple_assign (use_stmt)) + { + continue; + } + tree use_rhs = gimple_assign_rhs1 (use_stmt); + if (TREE_CODE (use_rhs) == COMPONENT_REF + && TREE_OPERAND (TREE_OPERAND (use_rhs, 0), 0) == rhs) + { + return true; + } + } + return false; +} + +/* Perform compression directly without checking null pointer. */ + +bool +ipa_struct_reorg::compress_candidate_without_check (gimple_stmt_iterator *gsi, + tree rhs, + tree &new_rhs) +{ + srtype *type = get_compression_candidate_type (rhs); + gcc_assert (type != NULL); + new_rhs = compress_ptr_to_offset (new_rhs, type, gsi); + return true; +} + +/* 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 = NULL_MAX_VALUE + 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); + + gimple* cur_stmt = as_a (cond); + edge e = split_block (cur_stmt->bb, cur_stmt); + 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 (POINTER_COMPRESSION_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 (is_direct_rewrite_chance_for_compression (rhs, new_rhs)) + { + return true; + } + else if (is_simplify_chance_for_compress_candidate (stmt, rhs)) + { + return compress_candidate_without_check (gsi, rhs, new_rhs); + } + else if (compress_candidate_with_check (gsi, rhs, new_rhs)) + { + return true; + } + return false; +} + +/* 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; +} + +/* Try decompress candidate without check. */ + +bool +ipa_struct_reorg::decompress_candidate_without_check (gimple_stmt_iterator *gsi, + tree lhs, tree rhs, + tree &new_lhs, + tree &new_rhs) +{ + imm_use_iterator imm_iter; + use_operand_p use_p; + bool processed = false; + + if (!gsi_one_before_end_p (*gsi)) + { + gsi_next (gsi); + gimple *next_stmt = gsi_stmt (*gsi); + if (gimple_assign_rhs_class (next_stmt) == GIMPLE_SINGLE_RHS) + { + tree next_rhs = gimple_assign_rhs1 (next_stmt); + /* If current lhs is used as rhs in the next stmt: + -> _1 = t->s + tt->s = _1. */ + if (lhs == next_rhs) + { + /* Check whether the lhs is only used in the next stmt. */ + unsigned use_num = 0; + processed = true; + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, lhs) + { + if (++use_num > 1) + { + processed = false; + break; + } + } + if (processed) + { + /* Only used in next stmt, direct reset new_rhs here. */ + TREE_TYPE (new_lhs) + = make_unsigned_type (POINTER_COMPRESSION_SIZE); + } + /* If the lhs used not only in next stmt return false here. */ + } + /* handle -> _1 = t->s + _2 = _1->s + In this case, _1 might not be nullptr. */ + else if (TREE_CODE (next_rhs) == COMPONENT_REF) + { + tree use_base = TREE_OPERAND (TREE_OPERAND (next_rhs, 0), 0); + if (use_base == lhs) + { + srtype *type = get_compression_candidate_type (rhs); + gcc_assert (type != NULL); + gsi_prev (gsi); + tree base = TREE_OPERAND (TREE_OPERAND (new_rhs, 0), 0); + tree new_mem_ref = build_simple_mem_ref (base); + tree new_ref = build3 (COMPONENT_REF, TREE_TYPE (new_rhs), + new_mem_ref, TREE_OPERAND (new_rhs, 1), + NULL_TREE); + new_rhs = decompress_offset_to_ptr (new_ref, type, gsi); + processed = true; + gsi_next (gsi); + } + } + } + gsi_prev (gsi); + return processed; + } + return false; +} + +/* 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 == NULL_VAL) + 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 null_type = make_unsigned_type (POINTER_COMPRESSION_SIZE); + gcond *cond = gimple_build_cond (EQ_EXPR, cond_lhs, + build_int_cst (null_type, + POINTER_COMPRESSION_NULL_VAL), + NULL_TREE, NULL_TREE); + gimple_set_location (cond, UNKNOWN_LOCATION); + gsi_insert_before (gsi, cond, GSI_SAME_STMT); + + /* Split bb. */ + gimple* cur_stmt = as_a (cond); + edge e = split_block (cur_stmt->bb, cur_stmt); + 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) +{ + + if (decompress_candidate_without_check (gsi, lhs, rhs, + new_lhs, new_rhs)) + { + return true; + } + 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 = is_compression_candidate (lhs); + bool r = is_compression_candidate (rhs); + if (!l && !r) + { + tree tmp_rhs = new_rhs == NULL_TREE ? rhs : new_rhs; + if (is_type_conversion_candidate_for_pc (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 (is_type_conversion_candidate_for_pc (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) { @@ -6108,6 +6936,12 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) fprintf (dump_file, "replaced with:\n"); for (unsigned i = 0; i < max_split && (newlhs[i] || newrhs[i]); i++) { + if (current_mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION) + { + 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)) { @@ -6182,6 +7016,15 @@ 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 (current_mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION + && 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; } @@ -6647,6 +7490,13 @@ ipa_struct_reorg::rewrite_functions (void) push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function = f; + if (current_mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION) + { + calculate_dominance_info (CDI_DOMINATORS); + loop_optimizer_init (0); + } + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nBefore rewrite: %dth_%s\n", @@ -6722,6 +7572,12 @@ ipa_struct_reorg::rewrite_functions (void) free_dominance_info (CDI_DOMINATORS); + if (current_mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION) + { + loop_optimizer_finalize (); + } + if (dump_file) { fprintf (dump_file, "\nAfter rewrite: %dth_%s\n", @@ -6756,6 +7612,10 @@ ipa_struct_reorg::execute_struct_relayout (void) { continue; } + if (get_type_name (types[i]->type) == NULL) + { + continue; + } retval |= ipa_struct_relayout (type, this).execute (); } @@ -6776,6 +7636,147 @@ 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::is_safe_void_cmp (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; + } + 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 1: + POINTER_COMPRESSION_SIZE = 8; // sizeof (uint8) + POINTER_COMPRESSION_NULL_VAL = 0xff; // max (uint8) + break; + case 2: + POINTER_COMPRESSION_SIZE = 16; // sizeof (uint16) + POINTER_COMPRESSION_NULL_VAL = 0xffff; // max (uint16) + break; + case 3: + POINTER_COMPRESSION_SIZE = 32; // sizeof (uint32) + POINTER_COMPRESSION_NULL_VAL = 0xffffffff; // max (uint32) + break; + default: + internal_error ("Invalid pointer compression level: %d", + param_pointer_compression_size); + } +} + unsigned int ipa_struct_reorg::execute (enum srmode mode) { @@ -6796,7 +7797,11 @@ ipa_struct_reorg::execute (enum srmode mode) { analyze_types (); } - + if (mode == STRUCT_LAYOUT_OPTIMIZE + && struct_layout_optimize_level >= POINTER_COMPRESSION) + { + check_and_prune_struct_for_pointer_compression (); + } ret = rewrite_functions (); } else if (mode == COMPLETE_STRUCT_RELAYOUT) @@ -6894,6 +7899,10 @@ public: virtual unsigned int execute (function *) { unsigned int ret = 0; + if (struct_layout_optimize_level >= POINTER_COMPRESSION) + { + init_pointer_size_for_pointer_compression (); + } ret = ipa_struct_reorg ().execute (STRUCT_LAYOUT_OPTIMIZE); return ret; } diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h index 936c0fa6ffc3c7fe0f378514440ccd214b741e13..357604d113f83da9862cb940bd44de56ef1f70b4 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -122,7 +122,9 @@ private: public: tree newtype[max_split]; + tree pc_gptr; bool visited; + bool pc_candidate; int has_alloc_array; // Constructors @@ -144,6 +146,7 @@ public: void analyze (void); bool has_dead_field (void); void mark_escape (escape_type, gimple *stmt); + void create_global_ptr_for_pointer_compression (); bool has_escaped (void) { return escapes != does_not_escape; diff --git a/gcc/params.opt b/gcc/params.opt index 9d1faa7ab5266d0e10faf6fe879a4c2b1e69d493..ca457f584329580ce59da2875e18b1ffedc5885e 100644 --- a/gcc/params.opt +++ b/gcc/params.opt @@ -984,4 +984,8 @@ High execution rate loops to be analyzed in prefetch (in%). Common Joined UInteger Var(param_prefetch_func_counts_threshold) Init(100) Param Optimization Threshold functions of cache miss counts to be analyzed in prefetching. +-param=param-pointer-compression-size= +Common Joined UInteger Var(param_pointer_compression_size) Init(3) IntegerRange(1, 3) Param Optimization +Target size of pointer compression. + ; This comment is to ensure we retain the blank line above. diff --git a/gcc/testsuite/gcc.dg/struct/csr_skip_void_struct_name.c b/gcc/testsuite/gcc.dg/struct/csr_skip_void_struct_name.c new file mode 100644 index 0000000000000000000000000000000000000000..c5e4968d91436e51304390c5ab519b6d5ba9e01d --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_skip_void_struct_name.c @@ -0,0 +1,53 @@ +// Structures without names should not be optimized +/* { dg-do compile } */ +#include +#include + +typedef struct +{ + int a; + float b; + double s1; + double s2; + double s3; + double s4; + double s5; + double s6; + double s7; + double s8; +} str_t1; + +#define N 1000 + +int num; + +int +main () +{ + int i, r; + + r = rand (); + num = r > N ? N : r; + str_t1 *p1 = calloc (num, sizeof (str_t1)); + + if (p1 == NULL) + return 0; + + for (i = 0; i < num; i++) + p1[i].a = 1; + + for (i = 0; i < num; i++) + p1[i].b = 2; + + for (i = 0; i < num; i++) + if (p1[i].a != 1) + abort (); + + for (i = 0; i < num; i++) + if (fabsf (p1[i].b - 2) > 0.0001) + abort (); + + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform in Complete Structure Relayout." "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/pc_cast_int.c b/gcc/testsuite/gcc.dg/struct/pc_cast_int.c new file mode 100644 index 0000000000000000000000000000000000000000..f9954eefd8610d659dacce752439d30079066d84 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/pc_cast_int.c @@ -0,0 +1,91 @@ +// Escape cast int for pointer compression +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; + network_t* net_add; +}; + + +const int MAX = 100; +network_t* net; +node_p node; + +int +main () +{ + net = (network_t*) calloc (1, sizeof(network_t)); + net->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + net->sorted_arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + net->nodes = (node_p) calloc (MAX, sizeof (node_t)); + net->arcs->id = 100; + + node = net->nodes; + node_p n1 = (node_p) 0x123456; + + for (unsigned i = 0; i < MAX; i++) + { + node->pred = n1; + node = node + 1; + } + + node = net->nodes; + + for (unsigned i = 0; i < MAX; i++) + { + if (node->pred != n1) + { + abort (); + } + node = node + 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform in pointer compression" "struct_layout" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/pc_compress_and_decomress.c b/gcc/testsuite/gcc.dg/struct/pc_compress_and_decomress.c new file mode 100644 index 0000000000000000000000000000000000000000..9cc7278b744cfc3c4c737e6857007ecb7d410320 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/pc_compress_and_decomress.c @@ -0,0 +1,90 @@ +// Support basic pointer compression and decompression +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; + network_t* net_add; +}; + + +const int MAX = 100; +network_t* net; +node_p node; + +int +main () +{ + net = (network_t*) calloc (1, sizeof(network_t)); + net->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + net->sorted_arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + net->nodes = (node_p) calloc (MAX, sizeof (node_t)); + net->arcs->id = 100; + + node = net->nodes; + + for (unsigned i = 0; i < MAX; i++) + { + node->pred = node; + node = node + 1; + } + + node = net->nodes; + + for (unsigned i = 0; i < MAX; i++) + { + if (node->pred != node) + { + abort (); + } + node = node + 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform in pointer compression is 1" "struct_layout" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/pc_ptr2void.c b/gcc/testsuite/gcc.dg/struct/pc_ptr2void.c new file mode 100644 index 0000000000000000000000000000000000000000..6cda10bb9f6c6d17f2d920f8a58ad460fe40bc15 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/pc_ptr2void.c @@ -0,0 +1,87 @@ +// Partially support escape_cast_void for pointer compression. +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs, sorted_arcs; + int x; + node_p nodes, stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +network_t* net = NULL; +int cnt = 0; + +__attribute__((noinline)) int +primal_feasible (network_t *net) +{ + void* stop; + node_t *node; + + node = net->nodes; + stop = (void *)net->stop_nodes; + for( node++; node < (node_t *)stop; node++ ) + { + net->x = 1; + printf( "PRIMAL NETWORK SIMPLEX: "); + } + return 0; +} + +int +main () +{ + net = (network_t*) calloc (1, 20); + net->nodes = calloc (MAX, sizeof (node_t)); + net->stop_nodes = net->nodes + MAX - 1; + cnt = primal_feasible( net ); + + net = (network_t*) calloc (1, 20); + if( !(net->arcs) ) + { + return -1; + } + return cnt; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform in pointer compression is 1" "struct_layout" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/pc_simple_rewrite_pc.c b/gcc/testsuite/gcc.dg/struct/pc_simple_rewrite_pc.c new file mode 100644 index 0000000000000000000000000000000000000000..20e15515e7c3e6681b6fdc3891176f0b88276619 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/pc_simple_rewrite_pc.c @@ -0,0 +1,112 @@ +// Check simplify rewrite chance for pointer compression and decompression +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; + network_t* net_add; +}; + + +const int MAX = 100; +network_t* net; +node_p node; +arc_p arc; + +int +main () +{ + net = (network_t*) calloc (1, sizeof(network_t)); + net->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + net->sorted_arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + net->nodes = (node_p) calloc (MAX, sizeof (node_t)); + net->arcs->id = 100; + + node = net->nodes; + arc = net->arcs; + + for (unsigned i = 0; i < MAX; i++) + { + arc->head = node; + arc->head->child = node; + node->potential = i + 1; + arc->cost = arc->head->potential; + arc->tail = node->sibling; + if (i % 2) + node->pred = net->nodes + i; + else + node->pred = NULL; + + if (node->pred && node->pred->child != NULL) + node->number = 0; + else + node->number = 1; + + node = node + 1; + arc = arc + 1; + } + + node = net->nodes; + arc = net->arcs; + + for (unsigned i = 0; i < MAX; i++) + { + node_p t = i % 2 ? node : NULL; + int tt = i % 2 ? 0 : 1; + if (arc->head->pred != t || arc->cost == 0 + || arc->tail != node->sibling || node->number != tt) + { + abort (); + } + arc = arc + 1; + node = node + 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform in pointer compression is 1" "struct_layout" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/pc_skip_void_struct_name.c b/gcc/testsuite/gcc.dg/struct/pc_skip_void_struct_name.c new file mode 100644 index 0000000000000000000000000000000000000000..2200e90b973a3142bcf82887e0359c2ee15824a7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/pc_skip_void_struct_name.c @@ -0,0 +1,53 @@ +// Structures without names should not be optimized +/* { dg-do compile } */ +#include +#include + +typedef struct +{ + int a; + float b; + double s1; + double s2; + double s3; + double s4; + double s5; + double s6; + double s7; + double s8; +} str_t1; + +#define N 1000 + +int num; + +int +main () +{ + int i, r; + + r = rand (); + num = r > N ? N : r; + str_t1 *p1 = calloc (num, sizeof (str_t1)); + + if (p1 == NULL) + return 0; + + for (i = 0; i < num; i++) + p1[i].a = 1; + + for (i = 0; i < num; i++) + p1[i].b = 2; + + for (i = 0; i < num; i++) + if (p1[i].a != 1) + abort (); + + for (i = 0; i < num; i++) + if (fabsf (p1[i].b - 2) > 0.0001) + abort (); + + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform in pointer compression" "struct_layout" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp index ac558581344bbc731d524b5b6aaca500bcab0bcf..852c37ff3f2d841b15664ef8a8c831285f295641 100644 --- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -85,6 +85,10 @@ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/complete_struct_relayout gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/dfe*.c]] \ "" "-fipa-struct-reorg=3 -fdump-ipa-all -flto-partition=one -fwhole-program" +# -fipa-struct-reorg=4 +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/pc_*.c]] \ + "" "-fipa-struct-reorg=4 -fdump-ipa-all-details -flto-partition=one -fwhole-program" + # All done. torture-finish dg-finish