diff options
author | Gary Oblock <gary@amperecomputing.com> | 2020-05-14 12:08:28 -0700 |
---|---|---|
committer | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-09-08 08:50:21 +0200 |
commit | a1624e8337fe6a70fd8b6c7da6fd6569a38294a8 (patch) | |
tree | 392de831410cf29923d49a917cee287b8f081c3c | |
parent | c6f774f496351e5ad7bad61dcfb292c6481bfae7 (diff) |
Back to the Marvell orginal code
-rw-r--r-- | gcc/ipa-structure-reorg.c | 1753 | ||||
-rw-r--r-- | gcc/ipa-structure-reorg.h | 187 |
2 files changed, 1307 insertions, 633 deletions
diff --git a/gcc/ipa-structure-reorg.c b/gcc/ipa-structure-reorg.c index 10e092b664d..5629cd7724d 100644 --- a/gcc/ipa-structure-reorg.c +++ b/gcc/ipa-structure-reorg.c @@ -34,225 +34,277 @@ along with GCC; see the file COPYING3. If not see #include <set> #include "ipa-structure-reorg.h" #include "dumpfile.h" +#include "pretty-print.h" #include "tree-pretty-print.h" #include "gimple-pretty-print.h" #include "langhooks.h" // TBD more includes go here -static void -setup_debug_flags (Info *); -static void -final_debug_info (Info *); -static unsigned int -reorg_analysis (Info_t *); -static void -reorg_analysis_debug (Info *, ReorgType *); -static bool -find_decls_and_types (Info_t *); -static void -disqualify_struct_in_struct_or_union (Info_t *); -static void -initial_reorg_debug (Info *info, ReorgType *reorg); -static void -disqualify_struct_in_struct_or_union_debug (Info *, ReorgType *); -static void -disq_str_in_str_or_union_helper (tree, std::set<tree> *, Info_t *); -static unsigned int -reorg_qualification (Info_t *); + +static void setup_debug_flags ( Info *); +static void final_debug_info ( Info *); +static unsigned int reorg_analysis ( Info_t *); +static void reorg_analysis_debug ( Info *, ReorgType *); +static bool find_decls_and_types ( Info_t *); +static void add_reorg_type( Info *, ReorgType *); +static void disqualify_struct_in_struct_or_union ( Info_t *); +static void initial_reorg_debug ( Info *info, ReorgType *reorg ); +static void disqualify_struct_in_struct_or_union_debug ( Info *, + ReorgType *); +static void disq_str_in_str_or_union_helper ( tree, + std::set<tree> *, + Info_t *); +static unsigned int reorg_qualification ( Info_t *); +static bool transformation_legality( Info_t *); +static void transformation_legality_debug ( Info *, ReorgType *); +static bool reorg_legality ( Info_t *); +static void reorg_forbidden ( gimple *, Info_t *); // Name changed and moved to its own file -// static void reorg_transformation ( Info_t *); +//static void reorg_transformation ( Info_t *); // made extern -// static void delete_reorgtype ( ReorgType_t *, Info_t *); -// static void undelete_reorgtype ( ReorgType_t *, Info_t *); -// static void remove_deleted_types ( Info *, ReorgFn); -static tree base_type_of (tree); -static bool -is_reorg_alloc_trigger (gimple *); -static ReorgType_t * -find_struct_type_ptr_to_struct (tree, Info *); -static ReorgType_t * -get_reorgtype_info (tree type, Info_t *info); -static void -print_reorg_with_msg (FILE *, ReorgType_t *, int, const char *); -static void -dump_reorg (ReorgType_t *reorg); -static void -print_reorgs (FILE *, int, Info *); -static void -print_reorg (FILE *, int, ReorgType_t *); -//-- debugging only -- -static const char *code_str (enum tree_code); +//static void delete_reorgtype ( ReorgType_t *, Info_t *); +//static void undelete_reorgtype ( ReorgType_t *, Info_t *); +//static void remove_deleted_types ( Info *, ReorgFn); +static enum ReorgOpTrans recognize_op ( tree, Info_t *); +static ReorgTransformation reorg_recognize ( gimple *, Info_t *); +static bool is_reorg_type ( tree, Info_t *); +static tree base_type_of ( tree); +static bool is_reorg_alloc_trigger ( gimple *); +static ReorgType_t *find_struct_type_ptr_to_struct ( tree, Info_t *); +static ReorgType_t *get_reorgtype_info ( tree, Info_t *); +static void print_reorg_with_msg ( FILE *, ReorgType_t *, int, const char *); +static void dump_reorg ( ReorgType_t *reorg); +static void print_reorgs ( FILE *, int, Info_t *); +static void print_reorg ( FILE *, int, ReorgType_t *); +static void print_progdecls ( FILE *, int, Info_t *); +static void print_progdecl ( FILE *, int, ProgDecl_t *); +static void print_program ( FILE *, int); +static void print_function ( FILE *, int, function *); +static ReorgType_t *get_reorgtype( gimple *stmt, Info_t *, int); +static int num_reorgtypes( gimple *, Info_t *); +static bool points_to_escape_analysis( Info_t *); +static bool uses_field_of_reorgtypes( gimple *, Info_t *); +//-- debugging only -- +#if DEBUGGING +static const char *code_str( enum tree_code); +static const char *type_name_to_str( tree); +static void handle_debug_indenting( int); +static int debug_indenting = 0; +#endif //---------------- Code Follows ---------------- - + static unsigned int -ipa_structure_reorg (void) +ipa_structure_reorg ( void) { - std::vector<ReorgType_t> Reorg_Type; - std::vector<ReorgType_t> Saved_Reorg_Type; - std::vector<ProgDecl_t> Prog_Decl; - std::map<tree, BoolPair_t> StructTypes; // TBD horrible type name + std::vector <ReorgType_t> Reorg_Type; + std::vector <ReorgType_t> Saved_Reorg_Type; + std::vector <ProgDecl_t> Prog_Decl; + std::map <tree,BoolPair_t> StructTypes; // TBD horrible type name - // DEBUG( "Running ipa_structure_reorg\n"); + //DEBUG_L( "Running ipa_structure_reorg\n"); + //INDENT(2); // TBD we must have run the IPA points-to analysis and // be running in a single LTRANS partition. Sanity check // these here. - Info_t info = {&Reorg_Type, &Saved_Reorg_Type, - &Prog_Decl, &StructTypes, - 0, 0.0, - false, false, - false, false, - false, false}; - - setup_debug_flags (&info); + Info_t info = { &Reorg_Type, + &Saved_Reorg_Type, + &Prog_Decl, + &StructTypes, + 0, + 0.0, + NULL, + false, + false, + false, + false, + false, + false, + false}; + + setup_debug_flags ( &info); + + if ( !reorg_analysis ( &info) ) + { + return true; + } - if (flag_ipa_dead_field_eliminate) - { - str_reorg_dead_field_eliminate (&info); + if ( reorg_qualification ( &info) ) + { + // Each of these do their own performance qualification and + // if they delete any types they must invoke restore_deleted_types + // so the next subpass has all the types to consider. + // Also, if they change the shape of a type the must create my + // type and update the ReorgTypes to reflect this. + + if ( flag_ipa_structure_reorg || flag_ipa_dead_field_eliminate ) { + str_reorg_dead_field_eliminate ( &info); } - - return true; - - if (reorg_qualification (&info)) - { - // Each of these do their own performance qualification and - // if they delete any types they must invoke restore_deleted_types - // so the next subpass has all the types to consider. - // Also, if they change the shape of a type the must create my - // type and update the ReorgTypes to reflect this. - - if (flag_ipa_structure_reorg || flag_ipa_field_reorder) - { - str_reorg_field_reorder (&info); - } - if (flag_ipa_structure_reorg || flag_ipa_instance_interleave) - { - str_reorg_instance_interleave (&info); - } + if ( flag_ipa_structure_reorg || flag_ipa_field_reorder ) { + str_reorg_field_reorder ( &info); + } + if ( flag_ipa_structure_reorg || flag_ipa_instance_interleave ) { + str_reorg_instance_interleave ( &info); } + } - final_debug_info (&info); + final_debug_info ( &info); return true; } static void -setup_debug_flags (Info *info) +setup_debug_flags ( Info *info) { // The normal way of doing this would be to // set the flags with dump_file && (dump_flags & TDF_XXX // where XXX is some level of dump control but // I think the code here would work better // (where each flag is set off the dump_flags.) - - if (dump_file) - { - info->show_all_reorg_cands = true; - info->show_all_reorg_cands_in_detail = dump_flags & TDF_DETAILS; - info->show_delete = dump_flags & TDF_DETAILS; - info->show_new_BBs = dump_flags & TDF_DETAILS; - info->show_transforms = dump_flags & TDF_DETAILS; - info->show_bounds = dump_flags & TDF_DETAILS; - } + + if ( dump_file ) + { + info->show_all_reorg_cands = true; + info->show_all_reorg_cands_in_detail = dump_flags & TDF_DETAILS; + info->show_prog_decls = true; + info->show_delete = dump_flags & TDF_DETAILS; + info->show_new_BBs = dump_flags & TDF_DETAILS; + info->show_transforms = dump_flags & TDF_DETAILS; + info->show_bounds = dump_flags & TDF_DETAILS; + info->reorg_dump_file = dump_file; + #if DEBUGGING + info->reorg_dump_file = stderr; + #endif + } } static void -final_debug_info (Info *info) +final_debug_info ( Info *info) { // TBD + if ( info->reorg_dump_file ) + { + print_program ( info->reorg_dump_file, 0); + } } static unsigned int -reorg_analysis (Info_t *info) +reorg_analysis ( Info_t *info) { struct cgraph_node *node; - find_decls_and_types (info); + find_decls_and_types ( info); // Skip computing numbOfGlobalArrays initially. // Skip computing numbOfLocalArrays initially. // Compute numbOfDynmAllocs per type in regtype - FOR_EACH_FUNCTION (node) - { - basic_block bb; + FOR_EACH_FUNCTION ( node) { + basic_block bb; - struct function *func = DECL_STRUCT_FUNCTION (node->decl); + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); - // This is a work around - if (func == NULL) - { - continue; - } + // This is a work around + if ( func == NULL ) + { + continue; + } - FOR_EACH_BB_FN (bb, func) + // Note,there is a major design issue with the design of this code. + // This can stand as is for now but it must be fixed relatively soon. + FOR_EACH_BB_FN ( bb, func) + { + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); + !gsi_end_p ( gsi); + gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + //DEBUG_L ( ""); + //DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + //INDENT(2); + if ( is_gimple_call ( stmt) ) { - gimple_stmt_iterator gsi; - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + // next line has issues but the mechanism is sound + tree t = *gimple_call_lhs_ptr ( stmt); + //DEBUG_A( "t %p\n", t); + // Calls to a function returning void are skipped. + if ( t != NULL ) + { + tree type = TREE_TYPE( t); + //tree bt = base_type_of ( t); + tree bt = base_type_of ( type); + if ( TREE_CODE( bt) != RECORD_TYPE ) { - gimple *stmt = gsi_stmt (gsi); - // DEBUG_F ( print_gimple_stmt, stderr, stmt, (dump_flags_t)-1); - // DEBUG ( "\n"); - if (is_gimple_call (stmt)) - { - // next line has issues but the mechanism is sound - tree t = *gimple_call_lhs_ptr (stmt); - // DEBUG( "t %p\n", t); - // Calls to a function returning void are skipped. - if (t != NULL) - { - tree bt = base_type_of (t); - if (TREE_CODE (bt) != RECORD_TYPE) - { - continue; - } - // find if in reorgtypes and get the info (in one call) - ReorgType_t *ri = get_reorgtype_info (bt, info); - if (ri != NULL && is_reorg_alloc_trigger (stmt)) - { - ri->numbOfDynmAllocs++; - } - } - } + //DEBUG_A( "TREE_CODE( bt) == %s\n", code_str( TREE_CODE ( bt))); + //DEBUG_A(""); + //DEBUG_F( print_generic_expr, stderr, bt, (dump_flags_t)-1); + //INDENT(-2); + continue; + } + // find if in reorgtypes and get the info (in one call) + ReorgType_t *ri = get_reorgtype_info ( bt, info); + if ( ri != NULL && is_reorg_alloc_trigger ( stmt) ) + { + //DEBUG_L( "Found allocaion: \n"); + //DEBUG_A( " Reorg: "); + //DEBUG_F( print_reorg, stderr, 0, ri); + //DEBUG("\n"); + ri->numbOfDynmAllocs++; } + } } + INDENT(-2); + } } + } + //DEBUG_L( "possible deletes:\n"); + //INDENT(2); // It's LOT clear to use an iterator here TBD - for (int i = 0; i < info->reorg_type->size (); i++) + for ( int i = 0; i < info->reorg_type->size (); i++ ) + { + int n = (*(info->reorg_type))[i].numbOfGlobalArrays + + (*(info->reorg_type))[i].numbOfLocalArrays + + (*(info->reorg_type))[i].numbOfDynmAllocs; + if ( n > 1 ) { - int n = (*(info->reorg_type))[i].numbOfGlobalArrays - + (*(info->reorg_type))[i].numbOfLocalArrays - + (*(info->reorg_type))[i].numbOfDynmAllocs; - if (n > 1) - { - (*(info->reorg_type))[i].multi_pool = true; - } - if (n == 0) - { - delete_reorgtype (&(*(info->reorg_type))[i], info); - } + (*(info->reorg_type))[i].multi_pool = true; + } + // Note when multi-pools are enabled the test should be + // "n == 0" but until then... + if ( n != 1 ) + { + delete_reorgtype ( &(*(info->reorg_type))[i], info); } + } + //INDENT(-2); - remove_deleted_types (info, &reorg_analysis_debug); + remove_deleted_types ( info, &reorg_analysis_debug); + + if ( info->show_all_reorg_cands ) + { + fprintf ( info->reorg_dump_file, "All Reorg Analysis ReorgTypes:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } return !info->reorg_type->empty (); } void -reorg_analysis_debug (Info *info, ReorgType *reorg) +reorg_analysis_debug ( Info *info, ReorgType *reorg ) { - if (info->show_delete) - { - print_reorg_with_msg (dump_file, reorg, 2, "Was not allocated"); - } + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "Was not allocated"); + } } -static bool -find_decls_and_types (Info_t *info) +static bool find_decls_and_types ( Info_t *info) { // Don't keep any structure types if they aren't // used in an array or have a pointer type (which @@ -261,101 +313,104 @@ find_decls_and_types (Info_t *info) // allocated arrays. // // This initializes them too of course. - + // Find all struct types for initial ReorgTypes // marking them all to initially be deleted. // This is done by walking all variables. std::set<tree> typeset; varpool_node *var; - FOR_EACH_VARIABLE (var) + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + //DEBUG_L( "Consider var->decl\n"); + //DEBUG_L( ""); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + tree base = base_type_of ( decl); + //DEBUG( "\n"); + //DEBUG_A(Base\n"); + //DEBUG_A( "TREE_CODE = %s, ", code_str( TREE_CODE ( base))); + //DEBUG_F( print_generic_expr, stderr, base, (dump_flags_t)-1); + //DEBUG( "\n"); + + if ( TREE_CODE ( base) == RECORD_TYPE ) { - tree decl = var->decl; - // DEBUG( "L# %d Consider var->decl\n", __LINE__); - // DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); - tree base = base_type_of (decl); - // DEBUG( "\nBase\n"); - // DEBUG( "TREE_CODE = %s\n", code_str( TREE_CODE ( base))); - // DEBUG_F( print_generic_expr, stderr, base, (dump_flags_t)-1); - // DEBUG( "\n"); - - if (TREE_CODE (base) == RECORD_TYPE) - { - // skip if found before - if (typeset.find (base) != typeset.end ()) - { - // DEBUG( "not found\n"); - continue; - } - else - { - // DEBUG( "found\n") - ; - } - ReorgType_t rt - = {0, base, 0, 0, 0, 0.0, 0.0, false, true, NULL, NULL}; - info->reorg_type->push_back (rt); - typeset.insert (base); // ??? - } + // skip if found before + if ( typeset.find ( base) != typeset.end () ) + { + //DEBUG_L( " not found\n"); + continue; + } else { + //DEBUG_L( " found\n") + ; + } + ReorgType_t rt = + { 0, base, 0, 0, 0, 0.0, 0.0, false, true, NULL, NULL }; + add_reorg_type ( info, &rt); + typeset.insert ( base); // ??? } - + } + // NOTE, the scheme above leaves out local variables so // I'll repeat the for the local variable of functions. // TBD Am I doing something actually, after this, that // is actually done here or above... it seems likely. - cgraph_node *node; - FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + cgraph_node* node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + tree decl; + unsigned i; + + node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + // enable this to see error in test_1_8 + //DEBUG_L( "function name = %s\n", lang_hooks.decl_printable_name ( node->decl, 2)); + if ( fn == NULL ) { - tree decl; - unsigned i; - - node->get_untransformed_body (); + //DEBUG( " EMPTY\n"); + continue; + } - struct function *fn = DECL_STRUCT_FUNCTION (node->decl); + //INDENT(2); + FOR_EACH_LOCAL_DECL ( fn, i, decl) + { + tree base = base_type_of ( decl); // enable this to see error in test_1_8 - // DEBUG( "L# %d, function name = %s\n", - //__LINE__, lang_hooks.decl_printable_name ( node->decl, 2)); - if (fn == NULL) - { - // DEBUG( " EMPTY\n"); + //DEBUG_L( "Consider local var decl\n"); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + //DEBUG( "\n"); + + if ( TREE_CODE ( base) == RECORD_TYPE) + { + if ( typeset.find ( base) != typeset.end () ) + { + //INDENT(-2) continue; } - FOR_EACH_LOCAL_DECL (fn, i, decl) - { - tree base = base_type_of (decl); - // enable this to see error in test_1_8 - // DEBUG( "L# %d Consider local var decl\n", __LINE__); - // DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); - // DEBUG( "\n"); - - if (TREE_CODE (base) == RECORD_TYPE) - { - if (typeset.find (base) != typeset.end ()) - { - continue; - } - - ReorgType_t rt - = {0, base, 0, 0, 0, 0.0, 0.0, false, true, NULL, NULL}; - info->reorg_type->push_back (rt); - typeset.insert (base); // ??? - } - } + ReorgType_t rt = + { 0, base, 0, 0, 0, 0.0, 0.0, false, true, NULL, NULL }; + add_reorg_type ( info, &rt); + typeset.insert ( base); // ??? + } } - + //INDENT(-2) + } + // We need this later for creating new types - for (std::set<tree>::iterator ti = typeset.begin (); ti != typeset.end (); - ti++) - { - (*(info->struct_types))[*ti] = {false, false}; - } + for ( std::set<tree>::iterator ti = typeset.begin (); + ti != typeset.end (); + ti++ ) + { + (*(info->struct_types))[*ti] = { false, false }; + } - if (info->show_all_reorg_cands_in_detail) - { - fprintf (dump_file, "All possible candidates:\n"); - print_reorgs (dump_file, 2, info); - } + if ( info->show_all_reorg_cands_in_detail ) + { + fprintf ( info->reorg_dump_file, "All possible candidates:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } // Scan all declarations for pointers to ReorgTypes // and in a later version array of them. When found @@ -363,54 +418,70 @@ find_decls_and_types (Info_t *info) // Note, there is no mechanism for looking at global declarations // so use FOR_EACH_VARIABLE instead. I'm not 100% this is the thing // actuall do here... but... - FOR_EACH_VARIABLE (var) + + //DEBUG_L( "Scan all global decls for pointers to ReorgTypes (possible deletes)\n"); + //INDENT(2); + + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + + //DEBUG_A( "look at each global var for undelete: "); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + //DEBUG( "\n"); + + tree type = TREE_TYPE ( decl); + ReorgType_t *rtype = find_struct_type_ptr_to_struct ( type, info); + if ( rtype != NULL ) { - tree decl = var->decl; - ReorgType_t *rtype = find_struct_type_ptr_to_struct (decl, info); - if (rtype != NULL) - { - undelete_reorgtype (rtype, info); - } + undelete_reorgtype ( rtype, info); } + } + //INDENT(-2); - FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + tree decl; + unsigned i; + + // Only need to do this once in the program and it was done + // above! + //Node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + + //DEBUG_L( "fn %p\n", fn); + //DEBUG_A(""); + //DEBUG_F( print_generic_decl, stderr, node->decl, (dump_flags_t)-1); + //DEBUG( "\n"); + // I don't know why this is coming up null.... but I'll + // skip it for now. + if ( fn == NULL ) { - tree decl; - unsigned i; - - // Only need to do this once in the program and it was done - // above! - // Node->get_untransformed_body (); - - struct function *fn = DECL_STRUCT_FUNCTION (node->decl); - - // DEBUG( "fn %p\n", fn); - // DEBUG_F( print_generic_decl, stderr, node->decl, (dump_flags_t)-1); - // DEBUG( "\n"); - // I don't know why this is coming up null.... but I'll - // skip it for now. - if (fn == NULL) - { - continue; - } - - FOR_EACH_LOCAL_DECL (fn, i, decl) - { - // Does this work... see tree.c:6132 - ReorgType_t *rtype = find_struct_type_ptr_to_struct (decl, info); - if (rtype != NULL) - { - undelete_reorgtype (rtype, info); - } - } + continue; } - if (info->show_all_reorg_cands) + //DEBUG_L( "possible deletes:\n"); + //INDENT(2); + FOR_EACH_LOCAL_DECL ( fn, i, decl) { - fprintf (dump_file, "All preliminary ReorgTypes:\n"); - // print_reorgs ( dump_file, 2, info); + // Does this work... see tree.c:6132 + tree type = TREE_TYPE ( decl); + ReorgType_t *rtype = find_struct_type_ptr_to_struct ( type, info); + if ( rtype != NULL ) + { + undelete_reorgtype ( rtype, info); + } } + //INDENT(-2); + } + if ( info->show_all_reorg_cands ) + { + fprintf ( info->reorg_dump_file, "All preliminary ReorgTypes:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } + // Scan all types in ReorgTypes for structure fields // and if they are pointers to a type Q in ReorgTypes // then clear the deletion mark of Q. Note, at this @@ -424,267 +495,729 @@ find_decls_and_types (Info_t *info) // that type. This of course assumes sane programming // practices and if they violate those structure reorg has // every right to punt. - for (std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); - ri != info->reorg_type->end (); ri++) + //DEBUG_L( "Examine imbedded pointers\n"); + //INDENT(2); + for ( std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); + ri++ ) + { + //DEBUG_A(""); + //DEBUG_F( dump_reorg, &(*ri)); + //DEBUG("\n"); + //INDENT(2); + for ( tree fld = TYPE_FIELDS ( ri->gcc_type); + fld; + fld = DECL_CHAIN ( fld) ) { - for (tree fld = TYPE_FIELDS (ri->gcc_type); fld; fld = DECL_CHAIN (fld)) - { - ReorgType_t *rtype = find_struct_type_ptr_to_struct (fld, info); - if (rtype != NULL) - { - undelete_reorgtype (rtype, info); - } - } + tree field_type = TREE_TYPE( fld); + ReorgType_t *rtype = + find_struct_type_ptr_to_struct ( field_type, info); + if ( rtype != NULL ) + { + undelete_reorgtype ( rtype, info); + } } + //INDENT(-2); + } + //INDENT(-2); - remove_deleted_types (info, &initial_reorg_debug); + remove_deleted_types ( info, &initial_reorg_debug); // Disqualifying structures in interior to structures is optional - // (see comment at end of type escape section) but if it's not + // (see comment at end of type escape section) but if it's not // done it commits the optimization to do something a little too // involved for the initial version. - disqualify_struct_in_struct_or_union (info); - - if (info->reorg_type->empty ()) + disqualify_struct_in_struct_or_union ( info); + + if ( info->reorg_type->empty () ) + { + if ( info->show_all_reorg_cands_in_detail ) { - if (info->show_all_reorg_cands_in_detail) - { - fprintf (dump_file, "find_decls_and_types: Found no types\n"); - } - return false; + fprintf ( info->reorg_dump_file, "find_decls_and_types: Found no types\n"); } + return false; + } // initialize ids of ReorgTypes int id = 0; - for (std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); - ri != info->reorg_type->end (); ri++) - { - ri->id = id; - id++; - } + for ( std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); + ri++ ) + { + ri->id = id; + id++; + } // Scan all declarations. If their type is in ReorgTypes // add them to ProgDecl. // Note, there is no mechanism for looking at global declarations // so use FOR_EACH_VARIABLE instead. I'm not 100% this is the thing // actuall do here... but... - FOR_EACH_VARIABLE (var) + //DEBUG_L( "ProgDecl global declarations:\n"); + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + tree type = base_type_of ( decl); + if ( TREE_CODE ( type ) == RECORD_TYPE && + get_reorgtype_info ( type, info) != NULL ) { - tree decl = var->decl; - tree type = base_type_of (decl); - if (TREE_CODE (type) == RECORD_TYPE - && get_reorgtype_info (type, info) != NULL) - { - ProgDecl_t decl_info; - decl_info.gcc_decl = decl; - info->prog_decl->push_back (decl_info); - } + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + info->prog_decl->push_back ( decl_info); + //DEBUG_A(""); + //DEBUG_F( print_progdecl, stderr, 2, &decl_info); } - - FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + } + + //DEBUG_L( "ProgDecl per function:\n"); + //INDENT(2); + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + //DEBUG_L( "function: %s\n", lang_hooks.decl_printable_name ( node->decl, 2)); + //INDENT(2); + // local declarations + unsigned i; + tree decl; + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + //DEBUG_L( "ProgDecl local declarations:\n"); + FOR_EACH_LOCAL_DECL ( fn, i, decl) { - // local declarations - unsigned i; - tree decl; - struct function *fn = DECL_STRUCT_FUNCTION (node->decl); - FOR_EACH_LOCAL_DECL (fn, i, decl) - { - tree type = base_type_of (decl); - if (TREE_CODE (type) == RECORD_TYPE - && get_reorgtype_info (type, info) != NULL) - { - ProgDecl_t decl_info; - decl_info.gcc_decl = decl; - info->prog_decl->push_back (decl_info); - } - } - - // parameter declarations - tree parm; - for (parm = DECL_ARGUMENTS (fn->decl); parm; parm = DECL_CHAIN (parm)) - { - tree decl = TREE_TYPE (parm); - tree type = base_type_of (decl); - if (TREE_CODE (type) == RECORD_TYPE - && get_reorgtype_info (type, info) != NULL) - { - ProgDecl_t decl_info; - decl_info.gcc_decl = decl; - info->prog_decl->push_back (decl_info); - } - } + tree type = base_type_of ( decl); + if ( TREE_CODE ( type ) == RECORD_TYPE && + get_reorgtype_info ( type, info) != NULL ) { + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + info->prog_decl->push_back ( decl_info); + //DEBUG_A(""); + //DEBUG_F( print_progdecl, stderr, 2, &decl_info); + } } - - if (info->show_all_reorg_cands_in_detail) + + // parameter declarations + //DEBUG_L( "ProgDecl parameter declarations:\n"); + tree parm; + for ( parm = DECL_ARGUMENTS ( fn->decl); + parm; + parm = DECL_CHAIN ( parm) ) { - fprintf (dump_file, "find_decls_and_types: Found the following types:\n"); - print_reorgs (dump_file, 2, info); + //tree decl = TREE_TYPE ( parm); + tree decl = parm; + tree type = base_type_of ( decl); + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + //DEBUG_A(""); + //DEBUG_F( print_progdecl, stderr, 2, &decl_info); + //DEBUG_A("TREE_CODE(type) == %s\n", code_str( TREE_CODE( type))); + if ( TREE_CODE ( type ) == RECORD_TYPE && + get_reorgtype_info ( type, info) != NULL ) + { + info->prog_decl->push_back ( decl_info); + //DEBUG_A(" -- Added to ProgDecls\n"); + } } + //INDENT(-2); + } + //INDENT(-2); + + if ( info->show_all_reorg_cands_in_detail ) + { + fprintf ( info->reorg_dump_file, "find_decls_and_types: Found the following types:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } + + if ( info->show_prog_decls ) + { + fprintf ( info->reorg_dump_file, "ProgDecls:\n"); + print_progdecls ( info->reorg_dump_file, 2, info); + } return true; } +static void +add_reorg_type ( Info *info, ReorgType_t *rt) +{ + info->reorg_type->push_back ( *rt); + // Remember the intial assumption is the type added will be deleted + // and is marked to be deleted. + info->num_deleted++; +} + void -disqualify_struct_in_struct_or_union (Info_t *info) +disqualify_struct_in_struct_or_union ( Info_t *info) { varpool_node *var; std::set<tree> typeset; - FOR_EACH_VARIABLE (var) + + //DEBUG_L( "In disqualify_struct_in_struct_or_union\n"); + //INDENT(2); + + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + + //DEBUG( "type %s\n", TYPE_NAME( TREE_TYPE( decl))); + + tree base = base_type_of ( decl); + if ( TREE_CODE ( base) == RECORD_TYPE + || TREE_CODE ( base) == UNION_TYPE ) { - tree decl = var->decl; - tree base = base_type_of (decl); - if (TREE_CODE (base) == RECORD_TYPE || TREE_CODE (base) == UNION_TYPE) - { - disq_str_in_str_or_union_helper (base, &typeset, info); - typeset.insert (base); - } + disq_str_in_str_or_union_helper ( base, &typeset, info); + typeset.insert ( base); + } + } + //INDENT(-2); + + // Repeating the above for local variables + cgraph_node* node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + tree decl; + unsigned i; + + node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + //DEBUG_L( "function name = %s\n", + // lang_hooks.decl_printable_name ( node->decl, 2)); + if ( fn == NULL ) + { + continue; } - remove_deleted_types (info, &disqualify_struct_in_struct_or_union_debug); + //INDENT(2); + FOR_EACH_LOCAL_DECL ( fn, i, decl) + { + tree base = base_type_of ( decl); + + //DEBUG_L( "local var decl: "); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + //DEBUG( "TREE_CODE( base) = %s\n", code_str( TREE_CODE ( base))); + + if ( TREE_CODE ( base) == RECORD_TYPE + || TREE_CODE ( base) == UNION_TYPE ) + { + disq_str_in_str_or_union_helper ( base, &typeset, info); + typeset.insert ( base); + } + } + //INDENT(-2); + } + + remove_deleted_types ( info, + &disqualify_struct_in_struct_or_union_debug); } static void -initial_reorg_debug (Info *info, ReorgType *reorg) +initial_reorg_debug ( Info *info, ReorgType *reorg ) { - if (info->show_delete) - { - print_reorg_with_msg (dump_file, reorg, 2, "No Pointer to Structure"); - } + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "No Pointer to Structure"); + } } + +static void +disqualify_struct_in_struct_or_union_debug ( Info *info, + ReorgType *reorg ) +{ + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "Interior Struct or Union"); + } +} + static void -disqualify_struct_in_struct_or_union_debug (Info *info, ReorgType *reorg) +disq_str_in_str_or_union_helper ( tree type, + std::set<tree> *typeset, + Info_t *info ) { - if (info->show_delete) + //DEBUG_L( "In disq_str_in_str_or_union_helper (possibele deletes)\n"); + //INDENT(2); + + if ( typeset->find ( type) != typeset->end ()) return; + tree fld; + for ( tree fld = TYPE_FIELDS( type); fld; fld = DECL_CHAIN ( fld) ) + { + //DEBUG_A( ": ", DECL_NAME( fld)); + //DEBUG_F( print_generic_decl, stderr, fld, (dump_flags_t)-1); + //DEBUG( " -- "); + //INDENT(2); + + // If we go to the base we end up disqualifying pointer to reorganizable + // structure. That's not what we want! + tree field_type = TREE_TYPE( fld); + //tree base = base_type_of ( field_type); + if ( TREE_CODE ( field_type) == RECORD_TYPE ) // base to field type { - print_reorg_with_msg (dump_file, reorg, 2, "Interior Struct or Union"); + //DEBUG( "RECORD\n"); + + ReorgType_t *rinfo = get_reorgtype_info ( field_type, info); // base to field type + if ( rinfo != NULL ) + { + delete_reorgtype ( rinfo, info); + } else { + disq_str_in_str_or_union_helper ( field_type, typeset, info ); // base to field type + typeset->insert ( field_type); // might be bug here -- base to field type + } + } else { + if ( TREE_CODE ( field_type) == UNION_TYPE ) { // base to field type + //DEBUG( "UNION\n"); + + disq_str_in_str_or_union_helper ( field_type, typeset, info ); // base to field type + typeset->insert ( field_type); // might be bug here -- base to field type + } else { + //DEBUG( "%s\n", code_str( TREE_CODE ( field_type)) ); // base to field type + } } + //INDENT(-2); + } + //INDENT(-2); + + return; +} + +static unsigned int +reorg_qualification ( Info_t *info) +{ + // TBD + // This only does a generic legality qualification and each + // subpass does its own performance qualification. + return reorg_legality( info); + } -static void -disq_str_in_str_or_union_helper (tree type, std::set<tree> *typeset, - Info_t *info) +// Return false if nothing qualified +bool +reorg_legality( Info_t *info) { + // NOTE, the following line does nothing unless + // we actually do our own points-to analysis + if( !points_to_escape_analysis( info) ) return false; + + return transformation_legality( info); +} + +// Return false if nothing qualified +bool +transformation_legality ( Info_t *info) { - if (typeset->find (type) != typeset->end ()) - return; - tree fld; - for (tree fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) + cgraph_node* node; + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + basic_block bb; + + FOR_EACH_BB_FN ( bb, func) { - if (TREE_CODE (fld) == RECORD_TYPE) + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); + !gsi_end_p ( gsi); + gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + int num = num_reorgtypes ( stmt, info); + if ( num != 0 ) { - ReorgType_t *rinfo = get_reorgtype_info (fld, info); - if (rinfo != NULL) - { - delete_reorgtype (rinfo, info); - } - else + if ( reorg_recognize( stmt, info) == Not_Supported ) + { + int i; + for ( i = 0; i < num; i++ ) { - disq_str_in_str_or_union_helper (fld, typeset, info); - typeset->insert (fld); // might be bug here + ReorgType_t *reorg_type = + get_reorgtype( stmt, info, i); + delete_reorgtype( reorg_type, info); } + } } - else + if ( uses_field_of_reorgtypes( stmt, info) ) { - if (TREE_CODE (fld) == UNION_TYPE) - { - disq_str_in_str_or_union_helper (fld, typeset, info); - typeset->insert (fld); // might be bug here - } + // This will mark types to be deleted if need be. + reorg_forbidden ( stmt, info ); } + } } - return; + } + + remove_deleted_types ( info, &transformation_legality_debug); + + return !info->reorg_type->empty (); } -static unsigned int -reorg_qualification (Info_t *info) +void +transformation_legality_debug ( Info *info, ReorgType *reorg ) { - // TBD - // This only does a generic legality qualification and each - // subpass does its own performance qualification. + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "Unallowed transformation"); + } } void -delete_reorgtype (ReorgType_t *rt, Info_t *info) +delete_reorgtype ( ReorgType_t *rt, Info_t *info ) { - if (!rt->delete_me) - { - info->num_deleted++; - rt->delete_me = true; - } + //DEBUG_L( "delete_reorgtype( %s ):", type_name_to_str( TYPE_NAME( rt->gcc_type))); + if ( !rt->delete_me ) + { + //DEBUG( "TO DELETE\n"); + info->num_deleted++; + rt->delete_me = true; + } else { + //DEBUG( "SKIP\n"); + } } void -undelete_reorgtype (ReorgType_t *rt, Info_t *info) +undelete_reorgtype ( ReorgType_t *rt, Info_t *info ) { - if (rt->delete_me) + //DEBUG_L( "undelete_reorgtype( %s ): ", type_name_to_str( TYPE_NAME( rt->gcc_type))); + if ( rt->delete_me ) + { + //DEBUG( "UNDELETE\n"); + info->num_deleted--; + rt->delete_me = false; + } else { + //DEBUG( "SKIP\n"); + } +} + +ReorgTransformation +reorg_recognize ( gimple *stmt, Info_t *info ) +{ + switch ( gimple_code( stmt) ) + { + case GIMPLE_ASSIGN: + { + tree lhs = gimple_assign_lhs ( stmt); + enum tree_code rhs_code = gimple_assign_rhs_code ( stmt); + + if ( gimple_assign_single_p ( stmt) ) + { + tree rhs = gimple_assign_rhs1 ( stmt); + switch ( recognize_op ( lhs, info) ) + { + case ReorgT_Pointer: // "a" + switch ( recognize_op ( rhs, info) ) + { + case ReorgT_Scalar: + if ( integer_zerop ( rhs) ) + { + return ReorgT_Ptr2Zero; + } + case ReorgT_Address: // "&x[i]" + return ReorgT_Adr2Ptr; + default: + return Not_Supported; + } + case ReorgT_Struct: // "s" + switch ( recognize_op ( rhs, info) ) + { + case ReorgT_Deref: // "*a" + case ReorgT_Array: // "x[i]" + return ReorgT_StrAssign; + default: + return Not_Supported; + } + case ReorgT_Deref: // "*a" + switch ( recognize_op ( rhs, info) ) + { + case ReorgT_Deref: // "*a" + case ReorgT_Struct: // "s" + case ReorgT_Array: // "x[i]" + return ReorgT_StrAssign; + default: + return Not_Supported; + } + case ReorgT_Array: // "x[i]" + switch ( recognize_op ( rhs, info) ) + { + case ReorgT_Struct: // "s" + case ReorgT_Deref: // "*a" + case ReorgT_Array: // "x[i]" + return ReorgT_StrAssign; + default: + return Not_Supported; + } + case ReorgT_Scalar: // "z" + switch ( recognize_op( rhs, info) ) + { + case ReorgT_Indirect: // "a->f" + case ReorgT_AryDir: // "x[i].f" + return ReorgT_ElemAssign; + default: + return Not_Supported; + } + case ReorgT_Indirect: // "a->f" + case ReorgT_AryDir: // "x[i].f" + switch ( recognize_op ( rhs, info) ) + { + case ReorgT_Scalar: // "z" + case ReorgT_Indirect: // "a->f" + case ReorgT_AryDir: // "x[i].f" + return ReorgT_ElemAssign; + default: + return Not_Supported; + } + default: + return Not_Supported; + } // switch ( recognize_op ( lhs, info) ) + } else { + tree op1 = gimple_assign_rhs1 ( stmt); + tree op2 = gimple_assign_rhs2 ( stmt); + + if ( gimple_assign_rhs3 ( stmt) != NULL ) + { + return Not_Supported; + } + + // TBD The parenthesis where a disaster here in the HL Design so + // double check this! + bool zero_case = + ( (POINTER_TYPE_P ( TREE_TYPE( op1)) && integer_zerop ( op2)) + || (POINTER_TYPE_P ( TREE_TYPE( op2)) && integer_zerop ( op1))) + && ( integer_zerop ( op1) || integer_zerop ( op2) ); + switch ( rhs_code ) + { + case POINTER_PLUS_EXPR: + return ReorgT_PtrPlusInt; + case POINTER_DIFF_EXPR: + return ReorgT_PtrDiff; + case EQ_EXPR: + return zero_case ? ReorgT_PtrNull : ReorgT_PtrEQ; + case NE_EXPR: + return zero_case ? ReorgT_PtrNotNull : ReorgT_PtrNE; + case LE_EXPR: + return ReorgT_PtrLE; + case LT_EXPR: + return ReorgT_PtrLT; + case GE_EXPR: + return ReorgT_PtrGE; + case GT_EXPR: + return ReorgT_PtrGT; + default: + return Not_Supported; + } + } // } else { + } + case GIMPLE_COND: // Similar to assign cases { - info->num_deleted--; - rt->delete_me = false; + tree op1 = gimple_assign_rhs1 ( stmt); + tree op2 = gimple_assign_rhs2( stmt); + enum tree_code cond_code = gimple_cond_code (stmt); + // TBD The parenthesis where a disaster here in the HL Design so + // double check this! + bool zero_case = + ( POINTER_TYPE_P ( TREE_TYPE ( op1)) && integer_zerop ( op2)) + || ( POINTER_TYPE_P ( TREE_TYPE ( op2)) && integer_zerop ( op1)); + switch ( cond_code ) + { + case EQ_EXPR: + return zero_case ? ReorgT_If_Null : ReorgT_IfPtrEQ; + case NE_EXPR: + return zero_case ? ReorgT_If_NotNull : ReorgT_IfPtrNE; + case LE_EXPR: + return ReorgT_IfPtrLE; + case LT_EXPR: + return ReorgT_IfPtrLT; + case GE_EXPR: + return ReorgT_IfPtrGE; + case GT_EXPR: + return ReorgT_IfPtrGT; + default: + return Not_Supported; + } } + case GIMPLE_CALL: + if ( gimple_call_builtin_p( stmt, BUILT_IN_CALLOC ) ) return ReorgT_Calloc; + if ( gimple_call_builtin_p( stmt, BUILT_IN_MALLOC ) ) return ReorgT_Malloc; + if ( gimple_call_builtin_p( stmt, BUILT_IN_REALLOC) ) return ReorgT_Realloc; + if ( gimple_call_builtin_p( stmt, BUILT_IN_FREE ) ) return ReorgT_Free; + return Not_Supported; + default: + return Not_Supported; + } } void -clear_deleted_types (Info *info) +clear_deleted_types( Info *info) { info->saved_reorg_type->clear (); } void -restore_deleted_types (Info *info) +restore_deleted_types ( Info *info) { - while (!info->saved_reorg_type->empty ()) - { - info->reorg_type->push_back (info->saved_reorg_type->back ()); - info->saved_reorg_type->pop_back (); - } + while ( !info->saved_reorg_type->empty () ) + { + info->reorg_type->push_back ( info->saved_reorg_type->back () ); + info->saved_reorg_type->pop_back (); + } } +// This routine disqualifies any and all reorg +// types in it that deserve disqualification. +// There are bizarre circumstances that could disqualify +// many types in a single statement. void -remove_deleted_types (Info *info, ReorgFn reorg_fn) +reorg_forbidden ( gimple *stmt, Info *info ) { - if (info->show_delete) - { - fprintf (dump_file, "DELETING REORG TYPES:\n"); - } + // The recognition of forbidden patterns must include casting a + // pointer (i.e. a pointer into a Reorg field) to something of a + // larger size (e.g. ints to longs) or doing arithmetic with them + // (e.g. bar->foo + k) since the later assumes the structure + // layout has not changed. +#if 0 + // TBD + switch( gimple_code( stmt) ) { + case : + } +#endif +} - if (info->num_deleted > 0) +void +remove_deleted_types ( Info *info, ReorgFn reorg_fn) +{ + //DEBUG_L( "remove_deleted_types: %d to delete of %d types\n", info->num_deleted, info->reorg_type->size ()); + if ( info->show_delete ) + { + fprintf ( info->reorg_dump_file, "DELETING REORG TYPES:\n"); + } + + if ( info->num_deleted > 0 ) + { + // Do delete here and run reorg_fn on each + // deleted type + int n = info->reorg_type->size (); + int to = 0; + //INDENT(2); + for ( int from = 0; from < n; from++ ) { - // Do delete here and run reorg_fn on each - // deleted type - int n = info->reorg_type->size (); - int to = 0; - for (int from = 0; from < n; from++) + //DEBUG_L( "%s ", type_name_to_str( TYPE_NAME( (*(info->reorg_type))[from].gcc_type))); + //DEBUG( "< from %d, to %d > - ", from, to); + + if ( !(*(info->reorg_type))[from].delete_me ) + { + //DEBUG( "NOT DELETED %d\n", from); + + // Save copy of removed entry + (*(info->reorg_type))[from].delete_me = false; + info->saved_reorg_type->push_back ( (*(info->reorg_type))[from]); + + // Delete by not copying deleted elements + if ( from != to ) { - if (!(*(info->reorg_type))[from].delete_me) - { - // Save copy of removed entry - (*(info->reorg_type))[from].delete_me = false; - info->saved_reorg_type->push_back ((*(info->reorg_type))[from]); - - // Delete by not copying deleted elements - if (from != to) - { - if (reorg_fn != NULL) - { - (*reorg_fn) (info, &(*(info->reorg_type))[from]); - } - (*(info->reorg_type))[to] = (*(info->reorg_type))[from]; - } - to++; - } + if ( reorg_fn != NULL ) + { + (*reorg_fn)( info, &(*(info->reorg_type))[ from]); + } + (*(info->reorg_type))[ to] = (*(info->reorg_type))[ from]; + //DEBUG_A( " move from -> to\n"); } - info->reorg_type->resize (n - info->num_deleted); - info->num_deleted = 0; + + to++; + } else { + //DEBUG( "DELETE %d\n", from); + } + } + //INDENT(-2); + info->reorg_type->resize ( n - info->num_deleted); + info->num_deleted = 0; + } +} + +enum ReorgOpTrans +recognize_op ( tree op, Info *info) +{ + enum tree_code op_code = TREE_CODE ( op); + tree type = TREE_TYPE ( op); + if ( POINTER_TYPE_P (type) ) + { + if ( is_reorg_type ( type, info) ) + { + return ReorgT_Pointer; + } else { + // This would be for when + // the field of a struct element + // is a pointer that's not a reorg + // point. I.e. ReorgT_ElemAssign. + return ReorgT_Scalar; + } + } + if ( op_code == RECORD_TYPE ) + { + // The assumption here is that this + // is a reorg type. + return ReorgT_Struct; + } + tree inter_op = TREE_OPERAND( op, 0); + if ( op_code == ADDR_EXPR + && TREE_CODE ( inter_op) == ARRAY_REF + && is_reorg_type ( inter_op, info) ) + { + return ReorgT_Address; + } + if ( op_code == COMPONENT_REF ) + { + if ( TREE_CODE( inter_op) == INDIRECT_REF ) + { + if ( is_reorg_type ( base_type_of ( type), info) ) + { + return ReorgT_Indirect; + } + // Just normal field reference otherwise... + return ReorgT_Scalar; + } else { + // Note, doesn't this ignore ARRAY_REF of this? + // I think it's OK at least until we start supporting + // multi-pools. + if ( is_reorg_type ( base_type_of ( type), info) ) + { + return ReorgT_AryDir; + } + // Just normal field reference otherwise... + return ReorgT_Scalar; + } + } + if ( op_code == ARRAY_REF ) + { + if ( is_reorg_type( base_type_of ( type), info) ) + { + return ReorgT_Array; } + return ReorgT_Scalar; + } + if( op_code == INDIRECT_REF ) + { + // Do we want to chase the base type? + // No, we care about (and transform) just + // *r and not **...r (where r is a ReorgType.) + if( is_reorg_type ( type, info) ) + { + return ReorgT_Deref; + } + return ReorgT_Scalar; + } + return ReorgT_Scalar; +} + +bool +is_reorg_type( tree rt, Info_t *info ) +{ + return get_reorgtype_info ( rt, info) != NULL; } static tree -base_type_of (tree type) +base_type_of ( tree type) { - for (; POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE - || TREE_CODE (type) == VAR_DECL; - type = TREE_TYPE (type)) - ; + for ( ; POINTER_TYPE_P ( type) || + TREE_CODE ( type) == ARRAY_TYPE || + TREE_CODE ( type) == VAR_DECL || + TREE_CODE ( type) == PARM_DECL + ; type = TREE_TYPE ( type) ); return type; } @@ -694,26 +1227,30 @@ base_type_of (tree type) // Also, we do handle realloc but it doesn't // create a new pool of memory so we ignore it here. static bool -is_reorg_alloc_trigger (gimple *stmt) +is_reorg_alloc_trigger ( gimple *stmt) { - return gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC); + return gimple_call_builtin_p ( stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p ( stmt, BUILT_IN_CALLOC); } -static ReorgType_t * -find_struct_type_ptr_to_struct (tree type, Info *info) +static ReorgType_t * +find_struct_type_ptr_to_struct ( tree type, Info *info) { - if (!POINTER_TYPE_P (type)) - { - return NULL; - } - for (; POINTER_TYPE_P (type); type = TREE_TYPE (type)) - ; - - if (TREE_CODE (type) == RECORD_TYPE) - { - return get_reorgtype_info (type, info); - } + //DEBUG_L( "find_struct_type_ptr_to_struct: "); + if ( !POINTER_TYPE_P ( type) ) { + //DEBUG(" bail\n"); + + return NULL; + } + for ( ; POINTER_TYPE_P ( type); type = TREE_TYPE ( type) ); + + if ( TREE_CODE ( type) == RECORD_TYPE ) { + //DEBUG( " look for info\n"); + + return get_reorgtype_info ( type, info); + } + //DEBUG(" fell through\n"); + return NULL; } @@ -721,159 +1258,297 @@ find_struct_type_ptr_to_struct (tree type, Info *info) // What's dicey about this is it may sort of work but then I // can see places where it wouldn't... The language has a say // in what types are equal so maybe language hooks are involved??? -bool -same_type_p (tree a, tree b) +bool same_type_p( tree a, tree b ) { - // DEBUG( "same_type_p:\n"); - // DEBUG( " a: TREE_CODE = %s, name = %p\n - // ",code_str(TREE_CODE(a)),TYPE_NAME(a)); DEBUG_F( print_generic_expr, stderr, - // a, (dump_flags_t)-1); DEBUG( "\n b TREE_CODE = %s, name = %p\n - // ",code_str(TREE_CODE(b)),TYPE_NAME(b)); DEBUG_F( print_generic_expr, stderr, - // b, (dump_flags_t)-1); DEBUG( "\n"); - gcc_assert (TREE_CODE (a) == RECORD_TYPE && TYPE_NAME (a) != 0); - gcc_assert (TREE_CODE (b) == RECORD_TYPE && TYPE_NAME (b) != 0); - return TYPE_NAME (a) == TYPE_NAME (b); + //DEBUG( "same_type_p:\n"); + //DEBUG( " a: TREE_CODE = %s, name = %p\n ",code_str(TREE_CODE(a)),TYPE_NAME(a)); + //DEBUG_F( print_generic_expr, stderr, a, (dump_flags_t)-1); + //DEBUG( "\n b TREE_CODE = %s, name = %p\n ",code_str(TREE_CODE(b)),TYPE_NAME(b)); + //DEBUG_F( print_generic_expr, stderr, b, (dump_flags_t)-1); + //DEBUG( "\n"); + + gcc_assert ( TREE_CODE ( a ) == RECORD_TYPE && TYPE_NAME ( a) != 0); + gcc_assert ( TREE_CODE ( b ) == RECORD_TYPE && TYPE_NAME ( b) != 0); + + bool ret = TYPE_NAME ( a) == TYPE_NAME ( b); + + //DEBUG( "returns %s\n", ret ? "true" : "false"); + + return ret; } // May need to add secondary map container to // look them up or even modify the container // type of ReorgType static ReorgType_t * -get_reorgtype_info (tree type, Info_t *info) +get_reorgtype_info ( tree type, Info_t* info) { + //DEBUG_L( "get_reorgtype_info\n"); + // Note, I'm going to use the most stupid and slowest possible way // to do this. The advanage is it will be super easy and almost // certainly correct. It will also almost certainly need to be // improved but I get something out there now. - for (std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); - ri != info->reorg_type->end (); ri++) + for ( std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); + ri++ ) + { + // TBD the internal docs lie and same_type_p doesn't exist + // (at least it's not available here at LTO time) + // so this is just a place holder until I can get an answer + // from the gcc community. Note, this is a big issue. + // Remember, the same_type_p here is my own temporary hack. + if ( same_type_p ( ri->gcc_type, type) ) { - // TBD the internal docs lie and same_type_p doesn't exist - // (at least it's not available here at LTO time) - // so this is just a place holder until I can get an answer - // from the gcc community. Note, this is a big issue. - // Remember, the same_type_p here is my own temporary hack. - if (same_type_p (ri->gcc_type, type)) - { - return &(*ri); - } + //DEBUG_A( " returns %p\n", &(*ri)); + + return &(*ri); } + } return NULL; } static void -print_reorg_with_msg (FILE *file, ReorgType_t *reorg, int leading_space, - const char *msg) +print_reorg_with_msg ( FILE *file, + ReorgType_t *reorg, + int leading_space, + const char *msg ) { - fprintf (file, "%*s%s:\n", leading_space, "", msg); - print_reorg (file, leading_space + 2, reorg); + fprintf ( file, "%*s%s:\n", leading_space, "", msg); + print_reorg ( file, leading_space + 2, reorg); } static void -dump_reorg (ReorgType_t *reorg) +dump_reorg ( ReorgType_t *reorg) { - print_reorg (stderr, 0, reorg); + print_reorg ( stderr, 0, reorg); } static void -print_reorgs (FILE *file, int leading_space, Info *info) +print_reorgs ( FILE *file, int leading_space, Info *info) { - for (int i = 0; i < info->reorg_type->size (); i++) - { - print_reorg (file, leading_space, &(*(info->reorg_type))[i]); - } + for ( int i = 0; i < info->reorg_type->size (); i++ ) { + print_reorg ( file, leading_space, &(*(info->reorg_type))[i]); + } } static void -print_reorg (FILE *file, int leading_space, ReorgType_t *reorg) +print_reorg ( FILE *file, int leading_space, ReorgType_t *reorg ) { // TBD // note if reorg_perf & regular_perf are nonzero // synthesize and display absolute_effect & raw_effect - + // Note, the following is a stub. const char *text - = identifier_to_locale (IDENTIFIER_POINTER (TYPE_NAME (reorg->gcc_type))); - fprintf (file, "%*s{ type:%s, id:%d, ... }\n", leading_space, "", text, - reorg->id); + = identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( reorg->gcc_type))); + fprintf ( file, "%*s{ type:%s, id:%d, %s, ... }\n", + leading_space, "", + text, + reorg->id, + reorg->multi_pool ? "multi" : "single" ); +} + +static void +print_progdecls ( FILE *file, int leading_space, Info * info) +{ + for ( int i = 0; i < info->prog_decl->size (); i++ ) { + print_progdecl ( file, leading_space, &(*(info->prog_decl))[i]); + } +} + +static void +print_progdecl ( FILE *file, int leading_space, ProgDecl_t *progdecl ) +{ + //INDENT(leading_space); + //DEBUG_L( "print_progdecl check TREE_CODE = %s\n", code_str( TREE_CODE( progdecl->gcc_decl))); + //INDENT(-leading_space); + //DEBUG_A(""); + fprintf ( file, "%*s", leading_space, ""); + print_generic_decl ( file, progdecl->gcc_decl, (dump_flags_t)-1); + fprintf ( file, "\n"); +} + +static void +print_program ( FILE *file, int leading_space ) +{ + struct cgraph_node *node; + fprintf ( file, "%*sProgram:\n", leading_space, ""); + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + // print func name + fprintf ( file, "%*sFunc: %s\n", leading_space + 2, "", lang_hooks.decl_printable_name ( node->decl, 2)); + print_function ( file, leading_space + 4, func); + } +} + +static void +print_function ( FILE *file, int leading_space, struct function *func) +{ + basic_block bb; + + FOR_EACH_BB_FN ( bb, func) + { + // print bb num + fprintf ( file, "%*sBB %d:\n", leading_space, "", bb->index ); + + // Tried to use function gimple_dump_bb is here instead of + // the following loop but it's worthless. + + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); + !gsi_end_p ( gsi); + gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + fprintf ( file, "%*s", leading_space + 2, "" ); + // Issue: for "if" this does not print the gotos. + print_gimple_stmt ( file, stmt, 0, (dump_flags_t)0); + } + } +} + +ReorgType_t * +get_reorgtype( gimple *stmt, Info_t *info, int i) +{ + // TBD - Straight forward + NULL; +} + +int +num_reorgtypes( gimple *stmt, Info_t *info) +{ + // TBD - Straight forward + 0; +} + +// Does there really need to be here? +// I suspect not. Perhaps if needed +// to run our own points-to analysis +// routine.... it would be invoked here. +// So, just to maintain a point to do this +// from, return true. +// Return false if nothing qualified +bool +points_to_escape_analysis( Info_t *info) +{ + // TBD + return true; +} + +bool +uses_field_of_reorgtypes( gimple *stmt, Info_t * info) +{ + // TBD + return false; } //-- debugging only -- static const char * -code_str (enum tree_code tc) +code_str( enum tree_code tc) { - switch (tc) + switch ( tc ) + { + case POINTER_TYPE: + return "POINTER_TYPE"; + case RECORD_TYPE: + return "RECORD_TYPE"; + case UNION_TYPE: + return "UNION_TYPE"; + case ARRAY_TYPE: + return "ARRAY_TYPE"; + case REFERENCE_TYPE: + return "REFERENCE_TYPE"; + case VOID_TYPE: + return "VOID_TYPE"; + case VAR_DECL: + return "VAR_DECL"; + case TYPE_DECL: + return "TYPE_DECL"; + case CONST_DECL: + return "CONST_DECL"; + case PARM_DECL: + return "PARM_DECL"; + case FIELD_DECL: + return "FIELD_DECL"; + case FUNCTION_DECL: + return "FUNCTION_DECL"; + case RESULT_DECL: + return "RESULT_DECL"; + default: + switch( TREE_CODE_CLASS( tc) ) { - case POINTER_TYPE: - return "POINTER_TYPE"; - case RECORD_TYPE: - return "RECORD_TYPE"; - case UNION_TYPE: - return "UNION_TYPE"; - case ARRAY_TYPE: - return "ARRAY_TYPE"; - case REFERENCE_TYPE: - return "REFERENCE_TYPE"; - case VAR_DECL: - return "VAR_DECL"; - case TYPE_DECL: - return "TYPE_DECL"; - case CONST_DECL: - return "CONST_DECL"; + case tcc_type: + return "class type"; + case tcc_declaration: + return "class declaration"; + case tcc_reference: + return "class reference"; default: - switch (TREE_CODE_CLASS (tc)) - { - case tcc_type: - return "class type"; - case tcc_declaration: - return "class declaration"; - case tcc_reference: - return "class reference"; - default: - return "unknown class"; - } + return "unknown class"; } + } +} + +const char * +type_name_to_str ( tree tn) +{ + gcc_assert ( tn != NULL ); + gcc_assert ( IDENTIFIER_POINTER ( tn) != NULL ); + return identifier_to_locale ( IDENTIFIER_POINTER ( tn)); +} + +#if DEBUGGING +static void +handle_debug_indenting ( int amount ) +{ + debug_indenting += amount; + debug_indenting = MAX ( debug_indenting, 0); } +#endif //---------------- Pass Control Follows ---------------- -const pass_data pass_data_ipa_structure_reorg = { - SIMPLE_IPA_PASS, /* type */ - "structure-reorg", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - // TV_IPA_SRA, /* tv_id */ // TBD +const pass_data pass_data_ipa_structure_reorg = +{ + IPA_PASS, /* type */ + "structure reorg", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + //TV_IPA_SRA, /* tv_id */ // TBD TV_IPA_STRUCTURE_REORG, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ //( TODO_dump_symtab | TODO_remove_functions ), /* todo_flags_finish */ // ??? - (TODO_update_ssa | TODO_update_address_taken | TODO_cleanup_cfg - | TODO_remove_unused_locals), - /* todo_flags_finish */ // ??? + 0, /* todo_flags_finish */ // ??? }; class pass_ipa_structure_reorg : public simple_ipa_opt_pass { public: - pass_ipa_structure_reorg (gcc::context *ctxt) - : simple_ipa_opt_pass (pass_data_ipa_structure_reorg, ctxt) + pass_ipa_structure_reorg ( gcc::context *ctxt) + : simple_ipa_opt_pass ( pass_data_ipa_structure_reorg, ctxt) {} /* opt_pass methods: */ - virtual bool gate (function *) - { - return ((flag_ipa_structure_reorg || flag_ipa_instance_interleave - || flag_ipa_field_reorder || flag_ipa_dead_field_eliminate)); - } + virtual bool gate ( function *) + { + return ( ( flag_ipa_structure_reorg || + flag_ipa_instance_interleave || + flag_ipa_field_reorder || + flag_ipa_dead_field_eliminate ) + && optimize ); + } - virtual unsigned int execute (function *) { return ipa_structure_reorg (); } + virtual unsigned int execute ( function *) { return ipa_structure_reorg (); } }; // class ipa_structure_reorg -// ipa_opt_pass_d * +//ipa_opt_pass_d * simple_ipa_opt_pass * -make_pass_ipa_structure_reorg (gcc::context *ctxt) +make_pass_ipa_structure_reorg ( gcc::context *ctxt) { - return new pass_ipa_structure_reorg (ctxt); + return new pass_ipa_structure_reorg ( ctxt); } diff --git a/gcc/ipa-structure-reorg.h b/gcc/ipa-structure-reorg.h index 910f066b677..9526929571a 100644 --- a/gcc/ipa-structure-reorg.h +++ b/gcc/ipa-structure-reorg.h @@ -20,135 +20,134 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ typedef struct ReorgType ReorgType_t; -struct ReorgType -{ - unsigned id; - tree gcc_type; - int numbOfGlobalArrays; // Statically allocated only - int numbOfLocalArrays; // Statically allocated only - int numbOfDynmAllocs; - double reorg_perf; - double regular_perf; - bool multi_pool; // single pool if not set - bool delete_me; - tree clone; // the base - tree pointer_rep; // new pointer format (multi-pool) +struct ReorgType { + unsigned id; + tree gcc_type; + int numbOfGlobalArrays; // Statically allocated only + int numbOfLocalArrays; // Statically allocated only + int numbOfDynmAllocs; + double reorg_perf; + double regular_perf; + bool multi_pool; // single pool if not set + bool delete_me; + tree clone; // the base + tree pointer_rep; // new pointer format (multi-pool) }; typedef struct ProgDecl ProgDecl_t; -struct ProgDecl -{ - tree gcc_decl; +struct ProgDecl { + tree gcc_decl; }; -enum ReorgOpTrans -{ - ReorgT_Address, // "&x[i]" - ReorgT_Pointer, // "a" - ReorgT_Struct, // "s" - ReorgT_Deref, // "*a" - ReorgT_Array, // "x[i]" - ReorgT_Scalar, // "z" - ReorgT_Indirect, // "a->f" - ReorgT_AryDir // "x[i].f" +enum ReorgOpTrans { + ReorgT_Address, // "&x[i]" + ReorgT_Pointer, // "a" + ReorgT_Struct, // "s" + ReorgT_Deref, // "*a" + ReorgT_Array, // "x[i]" + ReorgT_Scalar, // "z" + ReorgT_Indirect, // "a->f" + ReorgT_AryDir // "x[i].f" }; -enum CompressionControl -{ +enum CompressionControl { Initial, Subsequent }; -enum ReorgTransformation -{ - ReorgT_StrAssign, // "*a = x[i]", "x[i] = y[j]", "s = *a", etc. - ReorgT_ElemAssign, // "a->f = z", "z = x[i].f", etc. - ReorgT_If_Null, // "if(a == 0)..." - ReorgT_If_NotNull, // "if(a != 0)..." - ReorgT_IfPtrEQ, // "if(a == b)..." - ReorgT_IfPtrNE, // "if(a != b)..." - ReorgT_IfPtrLT, // "if(a < b)..." - ReorgT_IfPtrGT, // "if(a > b)..." - ReorgT_IfPtrLE, // "if(a <= b)..." - ReorgT_IfPtrGE, // "if(a >= b)..." - ReorgT_PtrPlusInt, // "a = b + i" - ReorgT_Ptr2Zero, // "a = 0" - ReorgT_PtrDiff, // "i = a - b" - ReorgT_Adr2Ptr, // "a = &x[i]" - ReorgT_PtrNull, // "x = a == 0" - ReorgT_PtrNotNull, // "x = a != 0" - ReorgT_PtrEQ, // "x = a == b" - ReorgT_PtrNE, // "x = a != b" - ReorgT_PtrLT, // "x = a < b" - ReorgT_PtrLE, // "x = a <= b" - ReorgT_PtrGT, // "x = a > b" - ReorgT_PtrGE, // "x = a >= b" - ReorgT_Malloc, // - ReorgT_Calloc, // - ReorgT_Realloc, // - ReorgT_Free, // +enum ReorgTransformation { + ReorgT_StrAssign, // "*a = x[i]", "x[i] = y[j]", "s = *a", etc. + ReorgT_ElemAssign, // "a->f = z", "z = x[i].f", etc. + ReorgT_If_Null, // "if(a == 0)..." + ReorgT_If_NotNull, // "if(a != 0)..." + ReorgT_IfPtrEQ, // "if(a == b)..." + ReorgT_IfPtrNE, // "if(a != b)..." + ReorgT_IfPtrLT, // "if(a < b)..." + ReorgT_IfPtrGT, // "if(a > b)..." + ReorgT_IfPtrLE, // "if(a <= b)..." + ReorgT_IfPtrGE, // "if(a >= b)..." + ReorgT_PtrPlusInt, // "a = b + i" + ReorgT_Ptr2Zero, // "a = 0" + ReorgT_PtrDiff, // "i = a - b" + ReorgT_Adr2Ptr, // "a = &x[i]" + ReorgT_PtrNull, // "x = a == 0" + ReorgT_PtrNotNull, // "x = a != 0" + ReorgT_PtrEQ, // "x = a == b" + ReorgT_PtrNE, // "x = a != b" + ReorgT_PtrLT, // "x = a < b" + ReorgT_PtrLE, // "x = a <= b" + ReorgT_PtrGT, // "x = a > b" + ReorgT_PtrGE, // "x = a >= b" + ReorgT_Malloc, // + ReorgT_Calloc, // + ReorgT_Realloc, // + ReorgT_Free, // Not_Supported }; // Added as design bug fix typedef struct BoolPair BoolPair_t; -struct BoolPair -{ +struct BoolPair { bool processed; bool layout_changed; }; typedef struct Info Info_t; -struct Info -{ - std::vector<ReorgType_t> *reorg_type; +struct Info { + std::vector <ReorgType_t> *reorg_type; // Added to by remove_deleted_types - std::vector<ReorgType_t> *saved_reorg_type; - std::vector<ProgDecl_t> *prog_decl; + std::vector <ReorgType_t> *saved_reorg_type; + std::vector <ProgDecl_t> *prog_decl; // Gcc doesn't have global decls readily available // so this holds them - std::map<tree, BoolPair_t> *struct_types; // desing bug fix - int num_deleted; - double total_cache_accesses; + std::map <tree,BoolPair_t> *struct_types; // desing bug fix + int num_deleted; + double total_cache_accesses; + FILE *reorg_dump_file; // Debug flags - bool show_all_reorg_cands; - bool show_all_reorg_cands_in_detail; - bool show_delete; - bool show_new_BBs; - bool show_transforms; - bool show_bounds; + bool show_all_reorg_cands; + bool show_all_reorg_cands_in_detail; + bool show_prog_decls; + bool show_delete; + bool show_new_BBs; + bool show_transforms; + bool show_bounds; }; // This will perform a function on the supplied // reorg type. It's primarily to support debugging. -typedef void (*ReorgFn) (Info *, ReorgType_t *); - -extern int -str_reorg_dead_field_eliminate (Info *); -extern int -str_reorg_field_reorder (Info *); -extern int -str_reorg_instance_interleave (Info *); - -extern void -delete_reorgtype (ReorgType_t *, Info_t *); -extern void -undelete_reorgtype (ReorgType_t *, Info_t *); -extern void -clear_deleted_types (Info *); -extern void -restore_deleted_types (Info *); -extern void -remove_deleted_types (Info *, ReorgFn reorg_fn); +typedef void (*ReorgFn)( Info *, ReorgType_t *); + +extern int str_reorg_dead_field_eliminate ( Info *); +extern int str_reorg_field_reorder ( Info *); +extern int str_reorg_instance_interleave ( Info *); + +extern void delete_reorgtype ( ReorgType_t *, Info_t *); +extern void undelete_reorgtype ( ReorgType_t *, Info_t *); +extern void clear_deleted_types( Info *); +extern void restore_deleted_types ( Info *); +extern void remove_deleted_types ( Info *, ReorgFn reorg_fn); + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) // I have no intention of leaving this or uses of the // defined marcos in the code. However, some of uses // should obviously be converted to dump file information. -#if 1 -#define DEBUG(...) fprintf (stderr, __VA_ARGS__) -#define DEBUG_F(f, ...) f (__VA_ARGS__) +#define DEBUGGING 1 +#if DEBUGGING +// Line numbered +#define DEBUG_L(...) { fprintf( stderr, "L# %4d: %*s", __LINE__, debug_indenting, ""); fprintf( stderr, __VA_ARGS__); } +// Alinged with line numbered +#define DEBUG_A(...) { fprintf( stderr, "%*s", debug_indenting + 7, ""); fprintf( stderr, __VA_ARGS__); } +//With no indenting +#define DEBUG(...) { fprintf( stderr, __VA_ARGS__); } +#define DEBUG_F(f,...) f( __VA_ARGS__) +#define INDENT(a) handle_debug_indenting(a) #else +#define DEBUG_L(...) +#define DEBUG_A(...) #define DEBUG(...) #define DEBUG_F(...) +#define INDENT(a) #endif |