From 242229bbdc6beacddb4ed263c759d3ca52dae927 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Sat, 19 Jun 2004 15:33:06 +0000 Subject: [PATCH] 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. * 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. * 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. From-SVN: r83385 --- gcc/ChangeLog | 51 +++ gcc/Makefile.in | 7 +- gcc/builtins.c | 12 +- gcc/cfgexpand.c | 480 +++++++++++++++++++++++++++ gcc/expr.c | 89 ------ gcc/passes.c | 22 +- gcc/predict.def | 3 + gcc/tree-cfg.c | 68 ++-- gcc/tree-flow.h | 5 +- gcc/tree-mudflap.c | 765 ++++++++++++++++++++++++++------------------ gcc/tree-optimize.c | 51 +-- gcc/tree-sra.c | 3 +- 12 files changed, 1066 insertions(+), 490 deletions(-) create mode 100644 gcc/cfgexpand.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6402c1eb565..2b7258649b7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,54 @@ +2004-06-19 Jan Hubicka + Steven Bosscher + + 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 + + * 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 + + * 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 PR c++/16036 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 0c1981f4db8..75b36868bb8 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 2ae99ba6a83..7ccddedf7e6 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 00000000000..d9be28be17a --- /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 */ +}; + diff --git a/gcc/expr.c b/gcc/expr.c index 651d6cd0142..aad7b1cb5ae 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -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 d9596799b0e..a4fb35a2611 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 836d853b379..4b571630b3b 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 1fad3dabde6..243a654171b 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 d440c8bad6b..6ddf72c2cfc 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 8a57efbe676..732ef5e4106 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 =
. */ - 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 16ddd8fc368..30e1e7c25ef 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 060b7e84642..b5e4735e29d 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; -- 2.30.2