diff options
-rw-r--r-- | gcc/ChangeLog | 51 | ||||
-rw-r--r-- | gcc/Makefile.in | 7 | ||||
-rw-r--r-- | gcc/builtins.c | 12 | ||||
-rw-r--r-- | gcc/cfgexpand.c | 480 | ||||
-rw-r--r-- | gcc/expr.c | 89 | ||||
-rw-r--r-- | gcc/passes.c | 22 | ||||
-rw-r--r-- | gcc/predict.def | 3 | ||||
-rw-r--r-- | gcc/tree-cfg.c | 68 | ||||
-rw-r--r-- | gcc/tree-flow.h | 5 | ||||
-rw-r--r-- | gcc/tree-mudflap.c | 765 | ||||
-rw-r--r-- | gcc/tree-optimize.c | 51 | ||||
-rw-r--r-- | gcc/tree-sra.c | 3 |
12 files changed, 1066 insertions, 490 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6402c1e..2b72586 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,54 @@ +2004-06-19 Jan Hubicka <jh@suse.cz> + Steven Bosscher <stevenb@suse.de> + + CFG transparent RTL expansion: + * Makefile.in (cfgexpand.o): New object file. + (builtins.o): Add dependency on basic-block.h + * builtins.c: Include basic-block.h + (entry_of_function): New function. + (expand_builtin_apply_args, expand_builtin_saveargs): Use it. + * cfgexpand.c: New file. + * expr.c (execute_expand, pass_expand): Kill. + * pass.c (rest_of_compilation): Do not build CFG unless called from + coverage code. + * tree-cfg.c (delete_tree_cfg): Rename to.. + (delete_tree_cfg_annotations): ... this one; Do not remove the CFG itself. + * tree-flow.h (delete_tree_cfg_annotations): Declare. + (dleete_tree_cfg): Kill. + * tree-optimize.c (execute_rebuild_bind, pass_rebuild_bind): Kill. + (execute_del_cfg): Rename to... + (execute_free_datastructures): This one... + (pass_del_cfg): Rename to... + (pass_free_datastructures): ... this one; Do not kill PROP_cfg. + (init_tree_optimization_passes): Make cfg build and profiling to happen + unconditionally. + +2004-06-19 Steven Bosscher <stevenb@suse.de> + + * tree-mudflap.c (mf_decl_cache_locals): Skip labels before + inserting the cache variables. + + * tree-mudflap.c: Include headers to make basic_block available. + Move functions around such that related functions are near each + other. Add prototypes for all static functions. Add comments + briefly explaining what IR the mudflap1 and mudflap2 work on and + what they do. + (mudflap_function_decls): Rename to execute_mudflap_function_decls. + (mudflap_function_ops): Rename to execute_mudflap_function_ops. + (pass_mudflap_1, pass_mudflap_2): Update. + (mf_decl_cache_locals): Make it work on the CFG instead of the saved + function tree. + (mf_build_check_statement_for): Make it work on the CFG. + (mf_xform_derefs_1): Likewise. Cleanup code style. + (mf_xform_derefs): Likewise. + +2004-06-19 Jan Hubicka <jh@suse.cz> + + * tree-cfg.c (label_to_block): Invent the label destination for + undefined labels. + (cleanup_dead_labels): Update table in the case label_to_block added + new label. + 2004-06-18 Richard Henderson <rth@redhat.com> PR c++/16036 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 0c1981f..75b3686 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -916,7 +916,7 @@ OBJS-common = \ targhooks.o timevar.o toplev.o tracer.o tree.o tree-dump.o unroll.o \ varasm.o varray.o version.o vmsdbgout.o xcoffout.o alloc-pool.o \ et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o \ - rtl-profile.o tree-profile.o rtlhooks.o + rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o OBJS-md = $(out_object_file) OBJS-archive = $(EXTRA_OBJS) $(host_hook_obj) tree-inline.o \ @@ -1793,7 +1793,7 @@ dojump.o : dojump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_ builtins.o : builtins.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H)\ $(TREE_GIMPLE_H) $(FLAGS_H) $(TARGET_H) function.h $(REGS_H) $(EXPR_H) $(OPTABS_H) \ insn-config.h $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \ - except.h $(TM_P_H) $(PREDICT_H) libfuncs.h real.h langhooks.h + except.h $(TM_P_H) $(PREDICT_H) libfuncs.h real.h langhooks.h basic-block.h calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) \ $(EXPR_H) $(OPTABS_H) langhooks.h $(TARGET_H) \ libfuncs.h $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H) cgraph.h except.h @@ -1930,6 +1930,9 @@ cfg.o : cfg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(FLAGS_H) in function.h except.h $(GGC_H) $(TM_P_H) alloc-pool.h $(TIMEVAR_H) cfghooks.o: cfghooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ $(BASIC_BLOCK_H) $(CFGLAYOUT_H) $(TREE_FLOW_H) $(TIMEVAR_H) toplev.h +cfgexpand.o : cfgexpand.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ + $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) function.h $(TIMEVAR_H) $(TM_H) coretypes.h \ + $(TREE_DUMP_H) except.h langhooks.h cfgloop.h gt-tree-cfg.h tree-pass.h $(RTL_H) cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(FLAGS_H) \ insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \ function.h except.h $(GGC_H) $(TM_P_H) insn-config.h $(EXPR_H) diff --git a/gcc/builtins.c b/gcc/builtins.c index 2ae99ba..7ccdded 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -45,6 +45,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "tm_p.h" #include "target.h" #include "langhooks.h" +#include "basic-block.h" #define CALLED_AS_BUILT_IN(NODE) \ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10)) @@ -1206,6 +1207,13 @@ expand_builtin_apply_args_1 (void) return copy_addr_to_reg (XEXP (registers, 0)); } +/* Return RTX to emit after when we want to emit code on the entry of function. */ +static rtx +entry_of_function (void) +{ + return (n_basic_blocks ? BB_HEAD (ENTRY_BLOCK_PTR->next_bb) : get_insns ()); +} + /* __builtin_apply_args returns block of memory allocated on the stack into which is stored the arg pointer, structure value address, static chain, and all the registers that might @@ -1239,7 +1247,7 @@ expand_builtin_apply_args (void) chain current, so the code is placed at the start of the function. */ push_topmost_sequence (); - emit_insn_before (seq, NEXT_INSN (get_insns ())); + emit_insn_before (seq, NEXT_INSN (entry_of_function ())); pop_topmost_sequence (); return temp; } @@ -4010,7 +4018,7 @@ expand_builtin_saveregs (void) is inside a start_sequence, make the outer-level insn chain current, so the code is placed at the start of the function. */ push_topmost_sequence (); - emit_insn_after (seq, get_insns ()); + emit_insn_after (seq, entry_of_function ()); pop_topmost_sequence (); return val; diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c new file mode 100644 index 0000000..d9be28b --- /dev/null +++ b/gcc/cfgexpand.c @@ -0,0 +1,480 @@ +/* A pass for lowering trees to RTL. + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "tm_p.h" +#include "basic-block.h" +#include "function.h" +#include "expr.h" +#include "langhooks.h" +#include "tree-flow.h" +#include "timevar.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "except.h" +#include "flags.h" +/* Expand basic block BB from GIMPLE trees to RTL. */ + +static basic_block +expand_block (basic_block bb, FILE * dump_file) +{ + block_stmt_iterator bsi = bsi_start (bb); + tree stmt = NULL; + rtx note, last; + edge e; + + if (dump_file) + { + tree_register_cfg_hooks (); + dump_bb (bb, dump_file, 0); + rtl_register_cfg_hooks (); + } + + if (!bsi_end_p (bsi)) + stmt = bsi_stmt (bsi); + + if (stmt && TREE_CODE (stmt) == LABEL_EXPR) + { + last = get_last_insn (); + + expand_expr_stmt_value (stmt, 0, 0); + + /* Java emits line number notes in the top of labels. + ??? Make this go away once line number notes are obsoleted. */ + BB_HEAD (bb) = NEXT_INSN (last); + if (GET_CODE (BB_HEAD (bb)) == NOTE) + BB_HEAD (bb) = NEXT_INSN (BB_HEAD (bb)); + bsi_next (&bsi); + note = emit_note_after (NOTE_INSN_BASIC_BLOCK, BB_HEAD (bb)); + } + else + note = BB_HEAD (bb) = emit_note (NOTE_INSN_BASIC_BLOCK); + + NOTE_BASIC_BLOCK (note) = bb; + + e = bb->succ; + while (e) + { + edge next = e->succ_next; + + /* Clear EDGE_EXECUTABLE. This flag is never used in the backend. */ + e->flags &= ~EDGE_EXECUTABLE; + + /* At the moment not all abnormal edges match the RTL representation. + It is safe to remove them here as find_sub_basic_blocks will + rediscover them. In the future we should get this fixed properly. */ + if (e->flags & EDGE_ABNORMAL) + remove_edge (e); + + e = next; + } + + for (; !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree stmt = bsi_stmt (bsi); + + last = get_last_insn (); + + if (!stmt) + continue; + + /* Expand this statement, then evaluate the resulting RTL and + fixup the CFG accordingly. */ + switch (TREE_CODE (stmt)) + { + case COND_EXPR: + { + basic_block new_bb, dest; + edge new_edge; + edge true_edge; + edge false_edge; + tree pred = COND_EXPR_COND (stmt); + tree then_exp = COND_EXPR_THEN (stmt); + tree else_exp = COND_EXPR_ELSE (stmt); + rtx last = get_last_insn (); + + extract_true_false_edges_from_block (bb, &true_edge, &false_edge); + if (EXPR_LOCUS (stmt)) + { + emit_line_note (*(EXPR_LOCUS (stmt))); + if (cfun->dont_emit_block_notes) + record_block_change (TREE_BLOCK (stmt)); + } + + /* These flags have no purpose in RTL land. */ + true_edge->flags &= ~EDGE_TRUE_VALUE; + false_edge->flags &= ~EDGE_FALSE_VALUE; + + /* We can either have a pure conditional jump with one fallthru + edge or two-way jump that needs to be decomposed into two + basic blocks. */ + if (TREE_CODE (then_exp) == GOTO_EXPR + && TREE_CODE (else_exp) == NOP_EXPR) + { + jumpif (pred, label_rtx (GOTO_DESTINATION (then_exp))); + break; + } + if (TREE_CODE (else_exp) == GOTO_EXPR + && TREE_CODE (then_exp) == NOP_EXPR) + { + jumpifnot (pred, label_rtx (GOTO_DESTINATION (else_exp))); + break; + } + if (TREE_CODE (then_exp) != GOTO_EXPR + || TREE_CODE (else_exp) != GOTO_EXPR) + abort (); + + jumpif (pred, label_rtx (GOTO_DESTINATION (then_exp))); + last = get_last_insn (); + expand_expr (else_exp, const0_rtx, VOIDmode, 0); + + BB_END (bb) = last; + if (GET_CODE (BB_END (bb)) == BARRIER) + BB_END (bb) = PREV_INSN (BB_END (bb)); + update_bb_for_insn (bb); + + new_bb = create_basic_block (NEXT_INSN (last), get_last_insn (), bb); + dest = false_edge->dest; + redirect_edge_succ (false_edge, new_bb); + false_edge->flags |= EDGE_FALLTHRU; + new_bb->count = false_edge->count; + new_bb->frequency = EDGE_FREQUENCY (false_edge); + new_edge = make_edge (new_bb, dest, 0); + new_edge->probability = REG_BR_PROB_BASE; + new_edge->count = new_bb->count; + if (GET_CODE (BB_END (new_bb)) == BARRIER) + BB_END (new_bb) = PREV_INSN (BB_END (new_bb)); + update_bb_for_insn (new_bb); + + if (dump_file) + { + dump_bb (bb, dump_file, 0); + dump_bb (new_bb, dump_file, 0); + } + return new_bb; + } + + /* Update after expansion of sibling call. */ + case CALL_EXPR: + case MODIFY_EXPR: + case RETURN_EXPR: + expand_expr_stmt_value (stmt, 0, 0); + for (last = NEXT_INSN (last); last; last = NEXT_INSN (last)) + { + if (GET_CODE (last) == CALL_INSN && SIBLING_CALL_P (last)) + { + edge e; + int probability = 0; + gcov_type count = 0; + + do_pending_stack_adjust (); + e = bb->succ; + while (e) + { + edge next = e->succ_next; + + if (!(e->flags & (EDGE_ABNORMAL | EDGE_EH))) + { + if (e->dest != EXIT_BLOCK_PTR) + { + e->dest->count -= e->count; + e->dest->frequency -= EDGE_FREQUENCY (e); + if (e->dest->count < 0) + e->dest->count = 0; + if (e->dest->frequency < 0) + e->dest->frequency = 0; + } + count += e->count; + probability += e->probability; + remove_edge (e); + } + + e = next; + } + + /* This is somewhat ugly: the call_expr expander often emits instructions + after the sibcall (to perform the function return). These confuse the + find_sub_basic_blocks code, so we need to get rid of these. */ + last = NEXT_INSN (last); + if (GET_CODE (last) != BARRIER) + abort (); + while (NEXT_INSN (last)) + { + /* For instance an sqrt builtin expander expands if with + sibcall in the then and label for `else`. */ + if (GET_CODE (NEXT_INSN (last)) == CODE_LABEL) + break; + delete_insn (NEXT_INSN (last)); + } + e = make_edge (bb, EXIT_BLOCK_PTR, + EDGE_ABNORMAL | EDGE_SIBCALL); + e->probability += probability; + e->count += count; + BB_END (bb) = last; + update_bb_for_insn (bb); + if (NEXT_INSN (last)) + bb = create_basic_block (NEXT_INSN (last), get_last_insn (), bb); + else + return bb; + } + } + break; + + default: + expand_expr_stmt_value (stmt, 0, 0); + break; + } + } + + do_pending_stack_adjust (); + + /* Find the the block tail. The last insn is the block is the insn + before a barrier and/or table jump insn. */ + last = get_last_insn (); + if (GET_CODE (last) == BARRIER) + last = PREV_INSN (last); + if (JUMP_TABLE_DATA_P (last)) + last = PREV_INSN (PREV_INSN (last)); + BB_END (bb) = last; + + if (dump_file) + dump_bb (bb, dump_file, 0); + update_bb_for_insn (bb); + return bb; +} + + +/* Create a basic block for initialization code. */ + +static basic_block +construct_init_block (void) +{ + basic_block init_block, first_block; + edge e; + + expand_start_bindings_and_block (0, NULL_TREE); + + for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next) + if (e->dest == ENTRY_BLOCK_PTR->next_bb) + break; + + init_block = create_basic_block (NEXT_INSN (get_insns ()), + get_last_insn (), + ENTRY_BLOCK_PTR); + init_block->frequency = ENTRY_BLOCK_PTR->frequency; + init_block->count = ENTRY_BLOCK_PTR->count; + if (e) + { + first_block = e->dest; + redirect_edge_succ (e, init_block); + e = make_edge (init_block, first_block, EDGE_FALLTHRU); + } + else + e = make_edge (init_block, EXIT_BLOCK_PTR, EDGE_FALLTHRU); + e->probability = REG_BR_PROB_BASE; + e->count = ENTRY_BLOCK_PTR->count; + + update_bb_for_insn (init_block); + return init_block; +} + + +/* Create a block containing landing pads and similar stuff. */ + +static void +construct_exit_block (void) +{ + rtx head = get_last_insn (); + rtx end; + basic_block exit_block; + edge e, e2, next; + + /* We hard-wired immediate_size_expand to zero above. + expand_function_end will decrement this variable. So, we set the + variable to one here, so that after the decrement it will remain + zero. */ + immediate_size_expand = 1; + + /* Make sure the locus is set to the end of the function, so that + epilogue line numbers and warnings are set properly. */ + if (cfun->function_end_locus.file) + input_location = cfun->function_end_locus; + + /* The following insns belong to the top scope. */ + record_block_change (DECL_INITIAL (current_function_decl)); + + expand_end_bindings (NULL_TREE, 1, 0); + + /* Generate rtl for function exit. */ + expand_function_end (); + + end = get_last_insn (); + if (head == end) + return; + while (NEXT_INSN (head) && GET_CODE (NEXT_INSN (head)) == NOTE) + head = NEXT_INSN (head); + exit_block = create_basic_block (NEXT_INSN (head), end, EXIT_BLOCK_PTR->prev_bb); + exit_block->frequency = EXIT_BLOCK_PTR->frequency; + exit_block->count = EXIT_BLOCK_PTR->count; + for (e = EXIT_BLOCK_PTR->pred; e; e = next) + { + next = e->pred_next; + if (!(e->flags & EDGE_ABNORMAL)) + redirect_edge_succ (e, exit_block); + } + e = make_edge (exit_block, EXIT_BLOCK_PTR, EDGE_FALLTHRU); + e->probability = REG_BR_PROB_BASE; + e->count = EXIT_BLOCK_PTR->count; + for (e2 = EXIT_BLOCK_PTR->pred; e2; e2 = e2->pred_next) + if (e2 != e) + { + e->count -= e2->count; + exit_block->count -= e2->count; + exit_block->frequency -= EDGE_FREQUENCY (e2); + } + if (e->count < 0) + e->count = 0; + if (exit_block->count < 0) + exit_block->count = 0; + if (exit_block->frequency < 0) + exit_block->frequency = 0; + update_bb_for_insn (exit_block); +} + +/* Called to move the SAVE_EXPRs for parameter declarations in a + nested function into the nested function. DATA is really the + nested FUNCTION_DECL. */ + +static tree +set_save_expr_context (tree *tp, + int *walk_subtrees, + void *data) +{ + if (TREE_CODE (*tp) == SAVE_EXPR && !SAVE_EXPR_CONTEXT (*tp)) + SAVE_EXPR_CONTEXT (*tp) = (tree) data; + /* Do not walk back into the SAVE_EXPR_CONTEXT; that will cause + circularity. */ + else if (DECL_P (*tp)) + *walk_subtrees = 0; + + return NULL; +} + + +/* Translate the intermediate representation contained in the CFG + from GIMPLE trees to RTL. + + We do conversion per basic block and preserve/update the tree CFG. + This implies we have to do some magic as the CFG can simultaneously + consist of basic blocks containing RTL and GIMPLE trees. This can + confuse the CFG hooks, so be curefull to not manipulate CFG during + the expansion. */ + +static void +tree_expand_cfg (void) +{ + basic_block bb, init_block; + sbitmap blocks; + + if (dump_file) + { + fprintf (dump_file, "\n;; Function %s", + (*lang_hooks.decl_printable_name) (current_function_decl, 2)); + fprintf (dump_file, " (%s)\n", + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); + } + + /* If the function has a variably modified type, there may be + SAVE_EXPRs in the parameter types. Their context must be set to + refer to this function; they cannot be expanded in the containing + function. */ + if (decl_function_context (current_function_decl) == current_function_decl + && variably_modified_type_p (TREE_TYPE (current_function_decl))) + walk_tree (&TREE_TYPE (current_function_decl), set_save_expr_context, + current_function_decl, NULL); + + /* Expand the variables recorded during gimple lowering. This must + occur before the call to expand_function_start to ensure that + all used variables are expanded before we expand anything on the + PENDING_SIZES list. */ + expand_used_vars (); + + /* Set up parameters and prepare for return, for the function. */ + expand_function_start (current_function_decl, 0); + + /* If this function is `main', emit a call to `__main' + to run global initializers, etc. */ + if (DECL_NAME (current_function_decl) + && MAIN_NAME_P (DECL_NAME (current_function_decl)) + && DECL_FILE_SCOPE_P (current_function_decl)) + expand_main_function (); + + /* Write the flowgraph to a dot file. */ + rtl_register_cfg_hooks (); + + init_block = construct_init_block (); + + FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR, next_bb) + bb = expand_block (bb, dump_file); + + construct_exit_block (); + + /* Convert from NOTE_INSN_EH_REGION style notes, and do other + sorts of eh initialization. Delay this until after the + initial rtl dump so that we can see the original nesting. */ + convert_from_eh_region_ranges (); + + rebuild_jump_labels (get_insns ()); + find_exception_handler_labels (); + + blocks = sbitmap_alloc (last_basic_block); + sbitmap_ones (blocks); + find_many_sub_basic_blocks (blocks); + purge_all_dead_edges (0); + sbitmap_free (blocks); + + compact_blocks (); +#ifdef ENABLE_CHECKING + verify_flow_info(); +#endif +} + +struct tree_opt_pass pass_expand = +{ + "expand", /* name */ + NULL, /* gate */ + tree_expand_cfg, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_EXPAND, /* tv_id */ + /* ??? If TER is enabled, we actually receive GENERIC. */ + PROP_gimple_leh | PROP_cfg, /* properties_required */ + PROP_rtl, /* properties_provided */ + PROP_gimple_leh, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ +}; + @@ -168,7 +168,6 @@ static void emit_single_push_insn (enum machine_mode, rtx, tree); #endif static void do_tablejump (rtx, enum machine_mode, rtx, rtx, rtx); static rtx const_vector_from_tree (tree); -static void execute_expand (void); /* Record for each mode whether we can move a register directly to or from an object of that mode in memory. If we can't, we won't try @@ -10213,92 +10212,4 @@ const_vector_from_tree (tree exp) return gen_rtx_raw_CONST_VECTOR (mode, v); } - -/* Called to move the SAVE_EXPRs for parameter declarations in a - nested function into the nested function. DATA is really the - nested FUNCTION_DECL. */ - -static tree -set_save_expr_context (tree *tp, - int *walk_subtrees, - void *data) -{ - if (TREE_CODE (*tp) == SAVE_EXPR && !SAVE_EXPR_CONTEXT (*tp)) - SAVE_EXPR_CONTEXT (*tp) = (tree) data; - /* Do not walk back into the SAVE_EXPR_CONTEXT; that will cause - circularity. */ - else if (DECL_P (*tp)) - *walk_subtrees = 0; - - return NULL; -} - - -static void -execute_expand (void) -{ - /* If the function has a variably modified type, there may be - SAVE_EXPRs in the parameter types. Their context must be set to - refer to this function; they cannot be expanded in the containing - function. */ - if (decl_function_context (current_function_decl) == current_function_decl - && variably_modified_type_p (TREE_TYPE (current_function_decl))) - walk_tree (&TREE_TYPE (current_function_decl), set_save_expr_context, - current_function_decl, NULL); - - /* Expand the variables recorded during gimple lowering. This must - occur before the call to expand_function_start to ensure that - all used variables are expanded before we expand anything on the - PENDING_SIZES list. */ - expand_used_vars (); - - /* Set up parameters and prepare for return, for the function. */ - expand_function_start (current_function_decl, 0); - - /* If this function is `main', emit a call to `__main' - to run global initializers, etc. */ - if (DECL_NAME (current_function_decl) - && MAIN_NAME_P (DECL_NAME (current_function_decl)) - && DECL_FILE_SCOPE_P (current_function_decl)) - expand_main_function (); - - /* Generate the RTL for this function. */ - expand_expr_stmt_value (DECL_SAVED_TREE (current_function_decl), 0, 0); - - /* We hard-wired immediate_size_expand to zero above. - expand_function_end will decrement this variable. So, we set the - variable to one here, so that after the decrement it will remain - zero. */ - immediate_size_expand = 1; - - /* Make sure the locus is set to the end of the function, so that - epilogue line numbers and warnings are set properly. */ - if (cfun->function_end_locus.file) - input_location = cfun->function_end_locus; - - /* The following insns belong to the top scope. */ - record_block_change (DECL_INITIAL (current_function_decl)); - - /* Generate rtl for function exit. */ - expand_function_end (); -} - -struct tree_opt_pass pass_expand = -{ - "expand", /* name */ - NULL, /* gate */ - execute_expand, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_EXPAND, /* tv_id */ - /* ??? If TER is enabled, we actually receive GENERIC. */ - PROP_gimple_leh, /* properties_required */ - PROP_rtl, /* properties_provided */ - PROP_cfg | PROP_gimple_leh, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0 /* todo_flags_finish */ -}; - - #include "gt-expr.h" diff --git a/gcc/passes.c b/gcc/passes.c index d959679..a4fb35a 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -1385,8 +1385,6 @@ rest_of_compilation (void) else finalize_block_changes (); - init_flow (); - /* Dump the rtl code if we are dumping rtl. */ if (open_dump_file (DFI_rtl, current_function_decl)) close_dump_file (DFI_rtl, print_rtl, get_insns ()); @@ -1443,11 +1441,21 @@ rest_of_compilation (void) timevar_push (TV_JUMP); open_dump_file (DFI_sibling, current_function_decl); - rebuild_jump_labels (get_insns ()); - find_exception_handler_labels (); - find_basic_blocks (get_insns (), max_reg_num (), dump_file); + /* ??? We may get called either via tree_rest_of_compilation when the CFG + is already built or directly (for instance from coverage code). + The direct callers shall be updated. */ + if (!basic_block_info) + { + init_flow (); + rebuild_jump_labels (get_insns ()); + find_exception_handler_labels (); + find_basic_blocks (get_insns (), max_reg_num (), dump_file); + } delete_unreachable_blocks (); +#ifdef ENABLE_CHECKING + verify_flow_info(); +#endif /* Turn NOTE_INSN_PREDICTIONs into branch predictions. */ if (flag_guess_branch_prob) @@ -1515,10 +1523,8 @@ rest_of_compilation (void) if (flag_guess_branch_prob) expected_value_to_br_prob (); - reg_scan (get_insns (), max_reg_num (), 0); - rebuild_jump_labels (get_insns ()); - find_basic_blocks (get_insns (), max_reg_num (), dump_file); delete_trivially_dead_insns (get_insns (), max_reg_num ()); + reg_scan (get_insns(), max_reg_num (), 0); if (dump_file) dump_flow_info (dump_file); cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) | CLEANUP_PRE_LOOP diff --git a/gcc/predict.def b/gcc/predict.def index 836d853..4b57163 100644 --- a/gcc/predict.def +++ b/gcc/predict.def @@ -116,3 +116,6 @@ DEF_PREDICTOR (PRED_NEGATIVE_RETURN, "negative return", HITRATE (96), 0) /* Branch ending with return; is probably not taken */ DEF_PREDICTOR (PRED_NULL_RETURN, "null return", HITRATE (90), 0) + +/* Branches to a mudflap bounds check are extremely unlikely. */ +DEF_PREDICTOR (PRED_MUDFLAP, "mudflap check", HITRATE (99), 0) diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 1fad3da..243a654 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -619,7 +619,21 @@ make_switch_expr_edges (basic_block bb) basic_block label_to_block (tree dest) { - return VARRAY_BB (label_to_block_map, LABEL_DECL_UID (dest)); + int uid = LABEL_DECL_UID (dest); + + /* We would die hard when faced by undefined label. Emit label to + very first basic block. This will hopefully make even the dataflow + and undefined variable warnings quite right. */ + if ((errorcount || sorrycount) && uid < 0) + { + block_stmt_iterator bsi = bsi_start (BASIC_BLOCK (0)); + tree stmt; + + stmt = build1 (LABEL_EXPR, void_type_node, dest); + bsi_insert_before (&bsi, stmt, BSI_NEW_STMT); + uid = LABEL_DECL_UID (dest); + } + return VARRAY_BB (label_to_block_map, uid); } @@ -756,6 +770,18 @@ update_eh_label (struct eh_region *region) } } +/* Given LABEL return the first label in the same basic block. */ +static tree +main_block_label (tree label) +{ + basic_block bb = label_to_block (label); + + /* label_to_block possibly inserted undefined label into the chain. */ + if (!label_for_bb[bb->index]) + label_for_bb[bb->index] = label; + return label_for_bb[bb->index]; +} + /* Cleanup redundant labels. This is a three-steo process: 1) Find the leading label for each block. 2) Redirect all references to labels to the leading labels. @@ -815,15 +841,14 @@ cleanup_dead_labels (void) case COND_EXPR: { tree true_branch, false_branch; - basic_block true_bb, false_bb; true_branch = COND_EXPR_THEN (stmt); false_branch = COND_EXPR_ELSE (stmt); - true_bb = label_to_block (GOTO_DESTINATION (true_branch)); - false_bb = label_to_block (GOTO_DESTINATION (false_branch)); - GOTO_DESTINATION (true_branch) = label_for_bb[true_bb->index]; - GOTO_DESTINATION (false_branch) = label_for_bb[false_bb->index]; + GOTO_DESTINATION (true_branch) + = main_block_label (GOTO_DESTINATION (true_branch)); + GOTO_DESTINATION (false_branch) + = main_block_label (GOTO_DESTINATION (false_branch)); break; } @@ -836,12 +861,8 @@ cleanup_dead_labels (void) /* Replace all destination labels. */ for (i = 0; i < n; ++i) - { - tree label = CASE_LABEL (TREE_VEC_ELT (vec, i)); - - CASE_LABEL (TREE_VEC_ELT (vec, i)) = - label_for_bb[label_to_block (label)->index]; - } + CASE_LABEL (TREE_VEC_ELT (vec, i)) + = main_block_label (CASE_LABEL (TREE_VEC_ELT (vec, i))); break; } @@ -849,13 +870,12 @@ cleanup_dead_labels (void) /* We have to handle GOTO_EXPRs until they're removed, and we don't remove them until after we've created the CFG edges. */ case GOTO_EXPR: - { - tree label = GOTO_DESTINATION (stmt); - if (! computed_goto_p (stmt)) - GOTO_DESTINATION (stmt) = - label_for_bb[label_to_block (label)->index]; - break; - } + if (! computed_goto_p (stmt)) + { + GOTO_DESTINATION (stmt) + = main_block_label (GOTO_DESTINATION (stmt)); + break; + } default: break; @@ -2636,19 +2656,19 @@ disband_implicit_edges (void) } } - -/* Remove all the blocks and edges that make up the flowgraph. */ +/* Remove block annotations and other datastructures. */ void -delete_tree_cfg (void) +delete_tree_cfg_annotations (void) { + basic_block bb; if (n_basic_blocks > 0) free_blocks_annotations (); - free_basic_block_vars (); - basic_block_info = NULL; label_to_block_map = NULL; free_rbi_pool (); + FOR_EACH_BB (bb) + bb->rbi = NULL; } diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index d440c8b..6ddf72c 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -434,7 +434,7 @@ extern void bsi_replace (const block_stmt_iterator *, tree, bool); /* Location to track pending stmt for edge insertion. */ #define PENDING_STMT(e) ((e)->insns.t) -extern void delete_tree_cfg (void); +extern void delete_tree_cfg_annotations (void); extern void disband_implicit_edges (void); extern bool stmt_ends_bb_p (tree); extern bool is_ctrl_stmt (tree); @@ -582,6 +582,9 @@ void set_value_handle (tree, tree); void debug_value_expressions (tree); void print_value_expressions (FILE *, tree); +/* In tree-sra.c */ +void insert_edge_copies (tree stmt, basic_block bb); + #include "tree-flow-inline.h" #endif /* _TREE_FLOW_H */ diff --git a/gcc/tree-mudflap.c b/gcc/tree-mudflap.c index 8a57efb..732ef5e 100644 --- a/gcc/tree-mudflap.c +++ b/gcc/tree-mudflap.c @@ -26,7 +26,11 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "system.h" #include "coretypes.h" #include "tm.h" +#include "hard-reg-set.h" +#include "rtl.h" #include "tree.h" +#include "tm_p.h" +#include "basic-block.h" #include "flags.h" #include "function.h" #include "tree-inline.h" @@ -43,50 +47,31 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA /* Internal function decls */ -static void mf_xform_derefs (tree); -static void mf_xform_decls (tree, tree); -static void mf_init_extern_trees (void); -static void mf_decl_cache_locals (tree *); -static void mf_decl_clear_locals (void); +/* Helpers. */ +static tree mf_build_string (const char *string); static tree mf_varname_tree (tree); -static tree mx_xfn_xform_decls (tree *, int *, void *); - -static void mx_register_decls (tree, tree *); - - -/* extern mudflap functions */ - -static GTY ((param_is (union tree_node))) htab_t marked_trees = NULL; - - -/* Mark and return the given tree node to prevent further mudflap - transforms. */ -tree -mf_mark (tree t) -{ - void **slot; - - if (marked_trees == NULL) - marked_trees = htab_create_ggc (31, htab_hash_pointer, htab_eq_pointer, NULL); +static tree mf_file_function_line_tree (location_t *); - slot = htab_find_slot (marked_trees, t, INSERT); - *slot = t; - return t; -} +/* Initialization of all the mf-runtime.h extern decls. */ +static void mf_init_extern_trees (void); +/* Indirection-related instrumentation. */ +static void mf_decl_cache_locals (void); +static void mf_decl_clear_locals (void); +static void mf_xform_derefs (void); +static void execute_mudflap_function_ops (void); -int -mf_marked_p (tree t) -{ - void *entry; +/* Addressable variables instrumentation. */ +static void mf_xform_decls (tree, tree); +static tree mx_xfn_xform_decls (tree *, int *, void *); +static void mx_register_decls (tree, tree *); +static void execute_mudflap_function_decls (void); - if (marked_trees == NULL) - return 0; - entry = htab_find (marked_trees, t); - return (entry != NULL); -} +/* ------------------------------------------------------------------------ */ +/* Some generally helpful functions for mudflap instrumentation. */ +/* Build a reference to a literal string. */ static tree mf_build_string (const char *string) { @@ -106,192 +91,6 @@ mf_build_string (const char *string) return mf_mark (result); } -/* Perform the declaration-related mudflap tree transforms on the - given function. Update its DECL_SAVED_TREE. */ - -static void -mudflap_function_decls (void) -{ - if (mf_marked_p (current_function_decl)) - return; - - push_gimplify_context (); - - mf_init_extern_trees (); - mf_xform_decls (DECL_SAVED_TREE (current_function_decl), - DECL_ARGUMENTS (current_function_decl)); - - pop_gimplify_context (NULL); -} - -static bool -gate_mudflap (void) -{ - return flag_mudflap != 0; -} - -struct tree_opt_pass pass_mudflap_1 = -{ - "mudflap1", /* name */ - gate_mudflap, /* gate */ - mudflap_function_decls, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - PROP_gimple_any, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func /* todo_flags_finish */ -}; - - -/* Same as above, for the indirection-related transforms. */ - -static void -mudflap_function_ops (void) -{ - if (mf_marked_p (current_function_decl)) - return; - - push_gimplify_context (); - - /* In multithreaded mode, don't cache the lookup cache parameters. */ - if (! flag_mudflap_threads) - mf_decl_cache_locals (&DECL_SAVED_TREE (current_function_decl)); - - mf_xform_derefs (DECL_SAVED_TREE (current_function_decl)); - - if (! flag_mudflap_threads) - mf_decl_clear_locals (); - - pop_gimplify_context (NULL); -} - -struct tree_opt_pass pass_mudflap_2 = -{ - "mudflap2", /* name */ - gate_mudflap, /* gate */ - mudflap_function_ops, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - PROP_gimple_leh, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func /* todo_flags_finish */ -}; - -/* global tree nodes */ - -/* Global tree objects for global variables and functions exported by - mudflap runtime library. mf_init_extern_trees must be called - before using these. */ - -/* uintptr_t (usually "unsigned long") */ -static GTY (()) tree mf_uintptr_type; - -/* struct __mf_cache { uintptr_t low; uintptr_t high; }; */ -static GTY (()) tree mf_cache_struct_type; - -/* struct __mf_cache * const */ -static GTY (()) tree mf_cache_structptr_type; - -/* extern struct __mf_cache __mf_lookup_cache []; */ -static GTY (()) tree mf_cache_array_decl; - -/* extern const unsigned char __mf_lc_shift; */ -static GTY (()) tree mf_cache_shift_decl; - -/* extern const uintptr_t __mf_lc_mask; */ -static GTY (()) tree mf_cache_mask_decl; - -/* Their function-scope local shadows, used in single-threaded mode only. */ - -/* auto const unsigned char __mf_lc_shift_l; */ -static GTY (()) tree mf_cache_shift_decl_l; - -/* auto const uintptr_t __mf_lc_mask_l; */ -static GTY (()) tree mf_cache_mask_decl_l; - -/* extern void __mf_check (void *ptr, size_t sz, int type, const char *); */ -static GTY (()) tree mf_check_fndecl; - -/* extern void __mf_register (void *ptr, size_t sz, int type, const char *); */ -static GTY (()) tree mf_register_fndecl; - -/* extern void __mf_unregister (void *ptr, size_t sz); */ -static GTY (()) tree mf_unregister_fndecl; - - -/* Initialize the global tree nodes that correspond to mf-runtime.h - declarations. */ -static void -mf_init_extern_trees (void) -{ - static bool done = false; - - if (done) - return; - done = true; - - mf_uintptr_type = TREE_TYPE (mflang_lookup_decl ("uintptr_t")); - mf_cache_array_decl = mf_mark (mflang_lookup_decl ("__mf_lookup_cache")); - mf_cache_struct_type = TREE_TYPE (TREE_TYPE (mf_cache_array_decl)); - mf_cache_structptr_type = build_pointer_type (mf_cache_struct_type); - mf_cache_shift_decl = mf_mark (mflang_lookup_decl ("__mf_lc_shift")); - mf_cache_mask_decl = mf_mark (mflang_lookup_decl ("__mf_lc_mask")); - mf_check_fndecl = mflang_lookup_decl ("__mf_check"); - mf_register_fndecl = mflang_lookup_decl ("__mf_register"); - mf_unregister_fndecl = mflang_lookup_decl ("__mf_unregister"); -} - - - -/* Create and initialize local shadow variables for the lookup cache - globals. Put their decls in the *_l globals for use by - mf_build_check_statement_for. */ - -static void -mf_decl_cache_locals (tree* body) -{ - tree_stmt_iterator i = tsi_start (*body); - tree t; - - mf_cache_shift_decl_l - = mf_mark (create_tmp_var (TREE_TYPE (mf_cache_shift_decl), - "__mf_lookup_shift_l")); - - mf_cache_mask_decl_l - = mf_mark (create_tmp_var (TREE_TYPE (mf_cache_mask_decl), - "__mf_lookup_mask_l")); - - /* Build initialization nodes for them. */ - t = build (MODIFY_EXPR, TREE_TYPE (mf_cache_shift_decl_l), - mf_cache_shift_decl_l, mf_cache_shift_decl); - annotate_with_locus (t, DECL_SOURCE_LOCATION (current_function_decl)); - gimplify_stmt (&t); - tsi_link_before (&i, t, TSI_NEW_STMT); - - t = build (MODIFY_EXPR, TREE_TYPE (mf_cache_mask_decl_l), - mf_cache_mask_decl_l, mf_cache_mask_decl); - annotate_with_locus (t, DECL_SOURCE_LOCATION (current_function_decl)); - gimplify_stmt (&t); - tsi_link_before (&i, t, TSI_NEW_STMT); -} - - -static void -mf_decl_clear_locals (void) -{ - /* Unset local shadows. */ - mf_cache_shift_decl_l = NULL_TREE; - mf_cache_mask_decl_l = NULL_TREE; -} - /* Create a properly typed STRING_CST node that describes the given declaration. It will be used as an argument for __mf_register(). Try to construct a helpful string, including file/function/variable @@ -432,35 +231,229 @@ mf_file_function_line_tree (location_t *locus) } +/* global tree nodes */ + +/* Global tree objects for global variables and functions exported by + mudflap runtime library. mf_init_extern_trees must be called + before using these. */ + +/* uintptr_t (usually "unsigned long") */ +static GTY (()) tree mf_uintptr_type; + +/* struct __mf_cache { uintptr_t low; uintptr_t high; }; */ +static GTY (()) tree mf_cache_struct_type; + +/* struct __mf_cache * const */ +static GTY (()) tree mf_cache_structptr_type; + +/* extern struct __mf_cache __mf_lookup_cache []; */ +static GTY (()) tree mf_cache_array_decl; + +/* extern const unsigned char __mf_lc_shift; */ +static GTY (()) tree mf_cache_shift_decl; + +/* extern const uintptr_t __mf_lc_mask; */ +static GTY (()) tree mf_cache_mask_decl; + +/* Their function-scope local shadows, used in single-threaded mode only. */ + +/* auto const unsigned char __mf_lc_shift_l; */ +static GTY (()) tree mf_cache_shift_decl_l; + +/* auto const uintptr_t __mf_lc_mask_l; */ +static GTY (()) tree mf_cache_mask_decl_l; + +/* extern void __mf_check (void *ptr, size_t sz, int type, const char *); */ +static GTY (()) tree mf_check_fndecl; + +/* extern void __mf_register (void *ptr, size_t sz, int type, const char *); */ +static GTY (()) tree mf_register_fndecl; + +/* extern void __mf_unregister (void *ptr, size_t sz); */ +static GTY (()) tree mf_unregister_fndecl; + + +/* Initialize the global tree nodes that correspond to mf-runtime.h + declarations. */ +static void +mf_init_extern_trees (void) +{ + static bool done = false; + + if (done) + return; + done = true; + + mf_uintptr_type = TREE_TYPE (mflang_lookup_decl ("uintptr_t")); + mf_cache_array_decl = mf_mark (mflang_lookup_decl ("__mf_lookup_cache")); + mf_cache_struct_type = TREE_TYPE (TREE_TYPE (mf_cache_array_decl)); + mf_cache_structptr_type = build_pointer_type (mf_cache_struct_type); + mf_cache_shift_decl = mf_mark (mflang_lookup_decl ("__mf_lc_shift")); + mf_cache_mask_decl = mf_mark (mflang_lookup_decl ("__mf_lc_mask")); + mf_check_fndecl = mflang_lookup_decl ("__mf_check"); + mf_register_fndecl = mflang_lookup_decl ("__mf_register"); + mf_unregister_fndecl = mflang_lookup_decl ("__mf_unregister"); +} + + +/* ------------------------------------------------------------------------ */ +/* Memory reference transforms. Perform the mudflap indirection-related + tree transforms on the current function. + + This is the second part of the mudflap instrumentation. It works on + low-level GIMPLE using the CFG, because we want to run this pass after + tree optimizations have been performed, but we have to preserve the CFG + for expansion from trees to RTL. */ + +static void +execute_mudflap_function_ops (void) +{ + if (mf_marked_p (current_function_decl)) + return; + + push_gimplify_context (); + + /* In multithreaded mode, don't cache the lookup cache parameters. */ + if (! flag_mudflap_threads) + mf_decl_cache_locals (); + + mf_xform_derefs (); + + if (! flag_mudflap_threads) + mf_decl_clear_locals (); + + pop_gimplify_context (NULL); +} + +/* Create and initialize local shadow variables for the lookup cache + globals. Put their decls in the *_l globals for use by + mf_build_check_statement_for. */ + +static void +mf_decl_cache_locals (void) +{ + tree t, shift_init_stmts, mask_init_stmts; + tree_stmt_iterator tsi; + + /* Build the cache vars. */ + mf_cache_shift_decl_l + = mf_mark (create_tmp_var (TREE_TYPE (mf_cache_shift_decl), + "__mf_lookup_shift_l")); + + mf_cache_mask_decl_l + = mf_mark (create_tmp_var (TREE_TYPE (mf_cache_mask_decl), + "__mf_lookup_mask_l")); + + /* Build initialization nodes for the cache vars. We just load the + globals into the cache variables. */ + t = build (MODIFY_EXPR, TREE_TYPE (mf_cache_shift_decl_l), + mf_cache_shift_decl_l, mf_cache_shift_decl); + annotate_with_locus (t, DECL_SOURCE_LOCATION (current_function_decl)); + gimplify_to_stmt_list (&t); + shift_init_stmts = t; + + t = build (MODIFY_EXPR, TREE_TYPE (mf_cache_mask_decl_l), + mf_cache_mask_decl_l, mf_cache_mask_decl); + annotate_with_locus (t, DECL_SOURCE_LOCATION (current_function_decl)); + gimplify_to_stmt_list (&t); + mask_init_stmts = t; + + /* Anticipating multiple entry points, we insert the cache vars + initializers in each successor of the ENTRY_BLOCK_PTR. */ + for (tsi = tsi_start (shift_init_stmts); + ! tsi_end_p (tsi); + tsi_next (&tsi)) + insert_edge_copies (tsi_stmt (tsi), ENTRY_BLOCK_PTR); + + for (tsi = tsi_start (mask_init_stmts); + ! tsi_end_p (tsi); + tsi_next (&tsi)) + insert_edge_copies (tsi_stmt (tsi), ENTRY_BLOCK_PTR); + bsi_commit_edge_inserts (NULL); +} + + static void -mf_build_check_statement_for (tree addr, tree size, tree_stmt_iterator *iter, +mf_decl_clear_locals (void) +{ + /* Unset local shadows. */ + mf_cache_shift_decl_l = NULL_TREE; + mf_cache_mask_decl_l = NULL_TREE; +} + +static void +mf_build_check_statement_for (tree addr, tree size, + block_stmt_iterator *instr_bsi, location_t *locus, tree dirflag) { + tree_stmt_iterator head, tsi; tree ptrtype = TREE_TYPE (addr); - tree stmt, cond, t, u, v; + block_stmt_iterator bsi; + basic_block cond_bb, then_bb, join_bb; + edge e; + tree cond, t, u, v, l1, l2; tree mf_value; tree mf_base; tree mf_elem; + /* We first need to split the current basic block, and start altering + the CFG. This allows us to insert the statements we're about to + construct into the right basic blocks. The label l1 is the label + of the block for the THEN clause of the conditional jump we're + about to construct, and l2 is the ELSE clause, which is just the + continuation of the old statement stream. */ + l1 = create_artificial_label (); + l2 = create_artificial_label (); + cond_bb = bb_for_stmt (bsi_stmt (*instr_bsi)); + bsi = *instr_bsi; + bsi_prev (&bsi); + if (! bsi_end_p (bsi)) + { + e = split_block (cond_bb, bsi_stmt (bsi)); + cond_bb = e->src; + join_bb = e->dest; + } + else + { + join_bb = cond_bb; + cond_bb = create_empty_bb (join_bb->prev_bb); + e = make_edge (cond_bb, join_bb, 0); + } + e->flags = EDGE_FALSE_VALUE; + then_bb = create_empty_bb (cond_bb); + make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE); + make_edge (then_bb, join_bb, EDGE_FALLTHRU); + + /* We expect that the conditional jump we will construct will not + be taken very often as it basically is an exception condition. */ + predict_edge_def (then_bb->pred, PRED_MUDFLAP, NOT_TAKEN); + + /* Update dominance info. Note that bb_join's data was + updated by split_block. */ + if (dom_computed[CDI_DOMINATORS] >= DOM_CONS_OK) + { + set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); + set_immediate_dominator (CDI_DOMINATORS, join_bb, cond_bb); + } + /* Build our local variables. */ mf_value = create_tmp_var (ptrtype, "__mf_value"); mf_elem = create_tmp_var (mf_cache_structptr_type, "__mf_elem"); mf_base = create_tmp_var (mf_uintptr_type, "__mf_base"); /* Build: __mf_value = <address expression>. */ - stmt = build (MODIFY_EXPR, void_type_node, mf_value, addr); - if (locus != NULL) - annotate_with_locus (stmt, *locus); - gimplify_stmt (&stmt); - tsi_link_before (iter, stmt, TSI_SAME_STMT); + t = build (MODIFY_EXPR, void_type_node, mf_value, unshare_expr (addr)); + SET_EXPR_LOCUS (t, locus); + gimplify_to_stmt_list (&t); + head = tsi_start (t); + tsi = tsi_last (t); /* Build: __mf_base = (uintptr_t)__mf_value. */ - stmt = build (MODIFY_EXPR, void_type_node, mf_base, - build1 (NOP_EXPR, mf_uintptr_type, mf_value)); - if (locus != NULL) - annotate_with_locus (stmt, *locus); - gimplify_stmt (&stmt); - tsi_link_before (iter, stmt, TSI_SAME_STMT); + t = build (MODIFY_EXPR, void_type_node, mf_base, + build1 (NOP_EXPR, mf_uintptr_type, mf_value)); + SET_EXPR_LOCUS (t, locus); + gimplify_to_stmt_list (&t); + tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Build: __mf_elem = &__mf_lookup_cache [(__mf_base >> __mf_shift) & __mf_mask]. */ @@ -472,81 +465,120 @@ mf_build_check_statement_for (tree addr, tree size, tree_stmt_iterator *iter, TREE_TYPE (TREE_TYPE (mf_cache_array_decl)), mf_cache_array_decl, t); t = build1 (ADDR_EXPR, mf_cache_structptr_type, t); - stmt = build (MODIFY_EXPR, void_type_node, mf_elem, t); - if (locus != NULL) - annotate_with_locus (stmt, *locus); - gimplify_stmt (&stmt); - tsi_link_before (iter, stmt, TSI_SAME_STMT); + t = build (MODIFY_EXPR, void_type_node, mf_elem, t); + SET_EXPR_LOCUS (t, locus); + gimplify_to_stmt_list (&t); + tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Quick validity check. - if (__builtin_expect ((__mf_elem->low > __mf_base) - | (__mf_elem_high < __mf_base + sizeof(T) - 1), - 0)) - { - __mf_check (); - ... and only if single-threaded: - __mf_lookup_shift_1 = f...; - __mf_lookup_mask_l = ...; - } - */ - /* __mf_elem->low */ + if (__mf_elem->low > __mf_base + || (__mf_elem_high < __mf_base + sizeof(T) - 1)) + { + __mf_check (); + ... and only if single-threaded: + __mf_lookup_shift_1 = f...; + __mf_lookup_mask_l = ...; + } + + It is expected that this body of code is rarely executed so we mark + the edge to the THEN clause of the conditional jump as unlikely. */ + + /* Construct t <-- '__mf_elem->low > __mf_base'. */ t = build (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TYPE_FIELDS (mf_cache_struct_type)); + t = build (GT_EXPR, boolean_type_node, t, mf_base); + + /* Construct '__mf_elem->high < __mf_base + sizeof(T) - 1'. + + First build: + 1) u <-- '__mf_elem->high' + 2) v <-- '__mf_base + sizeof (T) - 1'. + + Then build 'u <-- (u < v). */ + - /* __mf_elem->high */ u = build (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TREE_CHAIN (TYPE_FIELDS (mf_cache_struct_type))); - /* __mf_base + sizeof (T) - 1 */ - v = size_binop (MINUS_EXPR, size, size_one_node); - v = convert (mf_uintptr_type, v); + v = convert (mf_uintptr_type, + size_binop (MINUS_EXPR, size, size_one_node)); v = fold (build (PLUS_EXPR, mf_uintptr_type, mf_base, v)); - t = build (TRUTH_OR_EXPR, boolean_type_node, - build (GT_EXPR, boolean_type_node, t, mf_base), - build (LT_EXPR, boolean_type_node, u, v)); + u = build (LT_EXPR, boolean_type_node, u, v); + + /* Build the composed conditional: t <-- 't || u'. Then store the + result of the evaluation of 't' in a temporary variable which we + can use as the condition for the conditional jump. */ + t = build (TRUTH_OR_EXPR, boolean_type_node, t, u); + cond = create_tmp_var (boolean_type_node, "__mf_unlikely_cond"); + t = build (MODIFY_EXPR, boolean_type_node, cond, t); + gimplify_to_stmt_list (&t); + tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); + + /* Build the conditional jump. 'cond' is just a temporary so we can + simply build a void COND_EXPR. We do need labels in both arms though. */ + t = build (COND_EXPR, void_type_node, cond, + build (GOTO_EXPR, void_type_node, tree_block_label (then_bb)), + build (GOTO_EXPR, void_type_node, tree_block_label (join_bb))); + SET_EXPR_LOCUS (t, locus); + tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); - /* Mark condition as UNLIKELY using __builtin_expect. */ - u = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); - u = tree_cons (NULL_TREE, convert (long_integer_type_node, t), u); - cond = build_function_call_expr (built_in_decls[BUILT_IN_EXPECT], u); + /* At this point, after so much hard work, we have only constructed + the conditional jump, - /* Build up the body of the cache-miss handling: - __mf_check(); refresh *_l vars. */ + if (__mf_elem->low > __mf_base + || (__mf_elem_high < __mf_base + sizeof(T) - 1)) - stmt = NULL; + The lowered GIMPLE tree representing this code is in the statement + list starting at 'head'. + + We can insert this now in the current basic block, ie. the one that + the statement we're instrumenting was originally in. */ + bsi = bsi_last (cond_bb); + for (tsi = head; ! tsi_end_p (tsi); tsi_next (&tsi)) + bsi_insert_after (&bsi, tsi_stmt (tsi), BSI_CONTINUE_LINKING); + + /* Now build up the body of the cache-miss handling: + + __mf_check(); + refresh *_l vars. + + This is the body of the conditional. */ u = tree_cons (NULL_TREE, mf_file_function_line_tree (locus), NULL_TREE); u = tree_cons (NULL_TREE, dirflag, u); u = tree_cons (NULL_TREE, size, u); u = tree_cons (NULL_TREE, mf_value, u); t = build_function_call_expr (mf_check_fndecl, u); - append_to_statement_list (t, &stmt); + gimplify_to_stmt_list (&t); + head = tsi_start (t); + tsi = tsi_last (t); if (! flag_mudflap_threads) { t = build (MODIFY_EXPR, void_type_node, mf_cache_shift_decl_l, mf_cache_shift_decl); - append_to_statement_list (t, &stmt); + tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); t = build (MODIFY_EXPR, void_type_node, mf_cache_mask_decl_l, mf_cache_mask_decl); - append_to_statement_list (t, &stmt); + tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); } - stmt = build (COND_EXPR, void_type_node, cond, stmt, build_empty_stmt ()); - if (locus != NULL) - annotate_with_locus (stmt, *locus); - gimplify_to_stmt_list (&stmt); - lower_stmt_body (stmt, NULL); - tsi_link_before (iter, stmt, TSI_SAME_STMT); + /* Insert the check code in the THEN block. */ + bsi = bsi_start (then_bb); + for (tsi = head; ! tsi_end_p (tsi); tsi_next (&tsi)) + bsi_insert_after (&bsi, tsi_stmt (tsi), BSI_CONTINUE_LINKING); + + *instr_bsi = bsi_start (join_bb); + bsi_next (instr_bsi); } static void -mf_xform_derefs_1 (tree_stmt_iterator *iter, tree *tp, +mf_xform_derefs_1 (block_stmt_iterator *iter, tree *tp, location_t *locus, tree dirflag) { tree type, ptr_type, addr, size, t; @@ -574,15 +606,14 @@ mf_xform_derefs_1 (tree_stmt_iterator *iter, tree *tp, { tree dom = TYPE_DOMAIN (TREE_TYPE (op0)); - /* Test for index in range. Break if not. */ - if (!dom) - break; - if (!TYPE_MIN_VALUE (dom) || !really_constant_p (TYPE_MIN_VALUE (dom))) - break; - if (!TYPE_MAX_VALUE (dom) || !really_constant_p (TYPE_MAX_VALUE (dom))) - break; - if (tree_int_cst_lt (op1, TYPE_MIN_VALUE (dom)) - || tree_int_cst_lt (TYPE_MAX_VALUE (dom), op1)) + /* Test for index in range. Break if not. */ + if (!dom + || (! TYPE_MIN_VALUE (dom) + || ! really_constant_p (TYPE_MIN_VALUE (dom))) + || (! TYPE_MAX_VALUE (dom) + || ! really_constant_p (TYPE_MAX_VALUE (dom))) + || (tree_int_cst_lt (op1, TYPE_MIN_VALUE (dom)) + || tree_int_cst_lt (TYPE_MAX_VALUE (dom), op1))) break; /* If we're looking at a non-external VAR_DECL, then the @@ -679,45 +710,74 @@ mf_xform_derefs_1 (tree_stmt_iterator *iter, tree *tp, } static void -mf_xform_derefs (tree fnbody) +mf_xform_derefs (void) { - tree_stmt_iterator i = tsi_start (fnbody); + basic_block bb, next; + block_stmt_iterator i; + int saved_last_basic_block = last_basic_block; - for (i = tsi_start (fnbody); !tsi_end_p (i); tsi_next (&i)) + bb = ENTRY_BLOCK_PTR ->next_bb; + do { - tree s = tsi_stmt (i); - - /* Only a few GIMPLE statements can reference memory. */ - switch (TREE_CODE (s)) - { - case MODIFY_EXPR: /* This includes INIT_EXPR after gimplification. */ - mf_xform_derefs_1 (&i, &TREE_OPERAND (s, 0), EXPR_LOCUS (s), - integer_one_node); - mf_xform_derefs_1 (&i, &TREE_OPERAND (s, 1), EXPR_LOCUS (s), - integer_zero_node); - break; - - case RETURN_EXPR: - if (TREE_OPERAND (s, 0) != NULL_TREE) - { - if (TREE_CODE (TREE_OPERAND (s, 0)) == MODIFY_EXPR) - mf_xform_derefs_1 (&i, &TREE_OPERAND (TREE_OPERAND (s, 0), 1), EXPR_LOCUS (s), - integer_zero_node); - else - mf_xform_derefs_1 (&i, &TREE_OPERAND (s, 0), EXPR_LOCUS (s), - integer_zero_node); - } - break; - - default: - ; - } + next = bb->next_bb; + for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) + { + tree s = bsi_stmt (i); + + /* Only a few GIMPLE statements can reference memory. */ + switch (TREE_CODE (s)) + { + case MODIFY_EXPR: + mf_xform_derefs_1 (&i, &TREE_OPERAND (s, 0), EXPR_LOCUS (s), + integer_one_node); + mf_xform_derefs_1 (&i, &TREE_OPERAND (s, 1), EXPR_LOCUS (s), + integer_zero_node); + break; + + case RETURN_EXPR: + if (TREE_OPERAND (s, 0) != NULL_TREE) + { + if (TREE_CODE (TREE_OPERAND (s, 0)) == MODIFY_EXPR) + mf_xform_derefs_1 (&i, &TREE_OPERAND (TREE_OPERAND (s, 0), 1), + EXPR_LOCUS (s), integer_zero_node); + else + mf_xform_derefs_1 (&i, &TREE_OPERAND (s, 0), EXPR_LOCUS (s), + integer_zero_node); + } + break; + + default: + ; + } + } + bb = next; } + while (bb && bb->index <= saved_last_basic_block); } /* ------------------------------------------------------------------------ */ -/* ADDR_EXPR transform */ +/* ADDR_EXPR transforms. Perform the declaration-related mudflap tree + transforms on the current function. + + This is the first part of the mudflap instrumentation. It works on + high-level GIMPLE because after lowering, all variables are moved out + of their BIND_EXPR binding context, and we lose liveness information + for the declarations we wish to instrument. */ + +static void +execute_mudflap_function_decls (void) +{ + if (mf_marked_p (current_function_decl)) + return; + push_gimplify_context (); + + mf_init_extern_trees (); + mf_xform_decls (DECL_SAVED_TREE (current_function_decl), + DECL_ARGUMENTS (current_function_decl)); + + pop_gimplify_context (NULL); +} /* This struct is passed between mf_xform_decls to store state needed during the traversal searching for objects that have their @@ -936,13 +996,41 @@ mf_xform_decls (tree fnbody, tree fnparams) /* ------------------------------------------------------------------------ */ +/* Externally visible mudflap functions. */ + + +/* Mark and return the given tree node to prevent further mudflap + transforms. */ +static GTY ((param_is (union tree_node))) htab_t marked_trees = NULL; + +tree +mf_mark (tree t) +{ + void **slot; + + if (marked_trees == NULL) + marked_trees = htab_create_ggc (31, htab_hash_pointer, htab_eq_pointer, NULL); + + slot = htab_find_slot (marked_trees, t, INSERT); + *slot = t; + return t; +} + +int +mf_marked_p (tree t) +{ + void *entry; + if (marked_trees == NULL) + return 0; + + entry = htab_find (marked_trees, t); + return (entry != NULL); +} /* Remember given node as a static of some kind: global data, function-scope static, or an anonymous constant. Its assembler - label is given. -*/ - + label is given. */ /* A list of globals whose incomplete declarations we encountered. Instead of emitting the __mf_register call for them here, it's @@ -1066,7 +1154,6 @@ mudflap_enqueue_constant (tree obj) } - /* Emit any file-wide instrumentation. */ void mudflap_finish_file (void) @@ -1093,5 +1180,43 @@ mudflap_finish_file (void) } +static bool +gate_mudflap (void) +{ + return flag_mudflap != 0; +} + +struct tree_opt_pass pass_mudflap_1 = +{ + "mudflap1", /* name */ + gate_mudflap, /* gate */ + execute_mudflap_function_decls, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_gimple_any, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func /* todo_flags_finish */ +}; + +struct tree_opt_pass pass_mudflap_2 = +{ + "mudflap2", /* name */ + gate_mudflap, /* gate */ + execute_mudflap_function_ops, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_gimple_leh, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_flow | TODO_verify_stmts + | TODO_dump_func /* todo_flags_finish */ +}; #include "gt-tree-mudflap.h" diff --git a/gcc/tree-optimize.c b/gcc/tree-optimize.c index 16ddd8f..30e1e7c 100644 --- a/gcc/tree-optimize.c +++ b/gcc/tree-optimize.c @@ -84,33 +84,6 @@ static struct tree_opt_pass pass_gimple = TODO_dump_func /* todo_flags_finish */ }; -/* Pass: replace the outermost BIND_EXPR. We removed all of them while - optimizing, but the tree->rtl expander requires it. */ - -static void -execute_rebuild_bind (void) -{ - DECL_SAVED_TREE (current_function_decl) - = build (BIND_EXPR, void_type_node, NULL_TREE, - DECL_SAVED_TREE (current_function_decl), NULL_TREE); -} - -static struct tree_opt_pass pass_rebuild_bind = -{ - NULL, /* name */ - NULL, /* gate */ - execute_rebuild_bind, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0 /* todo_flags_finish */ -}; - /* Gate: execute, or not, all of the non-trivial optimizations. */ static bool @@ -141,9 +114,8 @@ static struct tree_opt_pass pass_all_optimizations = passes. */ static void -execute_del_cfg (void) +execute_free_datastructures (void) { - basic_block bb; tree *chain; /* ??? This isn't the right place for this. Worse, it got computed @@ -160,27 +132,23 @@ execute_del_cfg (void) /* Re-chain the statements from the blocks. */ chain = &DECL_SAVED_TREE (current_function_decl); *chain = alloc_stmt_list (); - FOR_EACH_BB (bb) - { - append_to_statement_list_force (bb->stmt_list, chain); - } - /* And get rid of the cfg. */ - delete_tree_cfg (); + /* And get rid of annotations we no longer need. */ + delete_tree_cfg_annotations (); } -static struct tree_opt_pass pass_del_cfg = +static struct tree_opt_pass pass_free_datastructures = { NULL, /* name */ NULL, /* gate */ - execute_del_cfg, /* execute */ + execute_free_datastructures, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ 0, /* tv_id */ PROP_cfg, /* properties_required */ 0, /* properties_provided */ - PROP_cfg, /* properties_destroyed */ + 0, /* properties_destroyed */ 0, /* todo_flags_start */ 0 /* todo_flags_finish */ }; @@ -283,16 +251,16 @@ init_tree_optimization_passes (void) NEXT_PASS (pass_mudflap_1); NEXT_PASS (pass_lower_cf); NEXT_PASS (pass_lower_eh); + NEXT_PASS (pass_build_cfg); + NEXT_PASS (pass_tree_profile); NEXT_PASS (pass_all_optimizations); NEXT_PASS (pass_mudflap_2); - NEXT_PASS (pass_rebuild_bind); + NEXT_PASS (pass_free_datastructures); NEXT_PASS (pass_expand); NEXT_PASS (pass_rest_of_compilation); *p = NULL; p = &pass_all_optimizations.sub; - NEXT_PASS (pass_build_cfg); - NEXT_PASS (pass_tree_profile); NEXT_PASS (pass_referenced_vars); NEXT_PASS (pass_build_pta); NEXT_PASS (pass_build_ssa); @@ -335,7 +303,6 @@ init_tree_optimization_passes (void) NEXT_PASS (pass_del_ssa); NEXT_PASS (pass_nrv); NEXT_PASS (pass_remove_useless_vars); - NEXT_PASS (pass_del_cfg); *p = NULL; #undef NEXT_PASS diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index 060b7e8..b5e4735 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -56,7 +56,6 @@ enum sra_copy_mode { SCALAR_SCALAR, FIELD_SCALAR, SCALAR_FIELD }; /* Local functions. */ static inline bool can_be_scalarized_p (tree); -static inline void insert_edge_copies (tree stmt, basic_block bb); static tree create_scalar_copies (tree lhs, tree rhs, enum sra_copy_mode mode); static inline void scalarize_component_ref (tree, tree *tp); static void scalarize_structures (void); @@ -556,7 +555,7 @@ find_candidates_for_sra (void) has more than one edge, STMT will be replicated for each edge. Also, abnormal edges will be ignored. */ -static inline void +void insert_edge_copies (tree stmt, basic_block bb) { edge e; |