X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fgimple-low.c;h=89de67a088c1f12c556116936b1a52201c248d76;hb=ff84f6db4f5156191531c8d5226bd0ccfc12cf2a;hp=69f9894a747f6356212dce7ee2dcb154d0eece60;hpb=6354626cdef500a87d9d85c5fa399b8a996662fb;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index 69f9894a747..89de67a088c 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -1,12 +1,12 @@ /* Tree lowering pass. Lowers GIMPLE into unstructured form. - Copyright (C) 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -15,9 +15,8 @@ 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, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" @@ -49,16 +48,20 @@ struct lower_data /* A TREE_LIST of label and return statements to be moved to the end of the function. */ tree return_statements; + + /* True if the function calls __builtin_setjmp. */ + bool calls_builtin_setjmp; }; static void lower_stmt (tree_stmt_iterator *, struct lower_data *); static void lower_bind_expr (tree_stmt_iterator *, struct lower_data *); static void lower_cond_expr (tree_stmt_iterator *, struct lower_data *); static void lower_return_expr (tree_stmt_iterator *, struct lower_data *); +static void lower_builtin_setjmp (tree_stmt_iterator *); -/* Lowers the body of current_function_decl. */ +/* Lower the body of current_function_decl. */ -static void +static unsigned int lower_function_body (void) { struct lower_data data; @@ -105,11 +108,35 @@ lower_function_body (void) It now fills in for many such returns. Failure to remove this will result in incorrect results for coverage analysis. */ x = TREE_VALUE (t); -#ifdef USE_MAPPED_LOCATION SET_EXPR_LOCATION (x, UNKNOWN_LOCATION); -#else - SET_EXPR_LOCUS (x, NULL); -#endif + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + } + + /* If the function calls __builtin_setjmp, we need to emit the computed + goto that will serve as the unique dispatcher for all the receivers. */ + if (data.calls_builtin_setjmp) + { + tree disp_label, disp_var, arg; + + /* Build 'DISP_LABEL:' and insert. */ + disp_label = create_artificial_label (); + /* This mark will create forward edges from every call site. */ + DECL_NONLOCAL (disp_label) = 1; + current_function_has_nonlocal_label = 1; + x = build1 (LABEL_EXPR, void_type_node, disp_label); + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + + /* Build 'DISP_VAR = __builtin_setjmp_dispatcher (DISP_LABEL);' + and insert. */ + disp_var = create_tmp_var (ptr_type_node, "setjmpvar"); + arg = build_addr (disp_label, current_function_decl); + t = implicit_built_in_decls[BUILT_IN_SETJMP_DISPATCHER]; + t = build_call_expr (t, 1, arg); + x = build_gimple_modify_stmt (disp_var, t); + + /* Build 'goto DISP_VAR;' and insert. */ + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + x = build1 (GOTO_EXPR, void_type_node, disp_var); tsi_link_after (&i, x, TSI_CONTINUE_LINKING); } @@ -118,10 +145,13 @@ lower_function_body (void) = blocks_nreverse (BLOCK_SUBBLOCKS (data.block)); clear_block_marks (data.block); + return 0; } -struct tree_opt_pass pass_lower_cf = +struct gimple_opt_pass pass_lower_cf = { + { + GIMPLE_PASS, "lower", /* name */ NULL, /* gate */ lower_function_body, /* execute */ @@ -133,12 +163,12 @@ struct tree_opt_pass pass_lower_cf = PROP_gimple_lcf, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ + TODO_dump_func /* todo_flags_finish */ + } }; -/* Lowers the EXPR. Unlike gimplification the statements are not relowered +/* Lower the EXPR. Unlike gimplification the statements are not relowered when they are changed -- if this has to be done, the lowering routine must do it explicitly. DATA is passed through the recursion. */ @@ -158,14 +188,10 @@ lower_stmt_body (tree expr, struct lower_data *data) static void lower_omp_directive (tree_stmt_iterator *tsi, struct lower_data *data) { - tree clause, stmt; + tree stmt; stmt = tsi_stmt (*tsi); - clause = (TREE_CODE (stmt) >= OMP_PARALLEL && TREE_CODE (stmt) <= OMP_SINGLE) - ? OMP_CLAUSES (stmt) - : NULL_TREE; - lower_stmt_body (OMP_BODY (stmt), data); tsi_link_before (tsi, stmt, TSI_SAME_STMT); tsi_link_before (tsi, OMP_BODY (stmt), TSI_SAME_STMT); @@ -174,7 +200,7 @@ lower_omp_directive (tree_stmt_iterator *tsi, struct lower_data *data) } -/* Lowers statement TSI. DATA is passed through the recursion. */ +/* Lower statement TSI. DATA is passed through the recursion. */ static void lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) @@ -210,22 +236,47 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) case NOP_EXPR: case ASM_EXPR: - case MODIFY_EXPR: - case CALL_EXPR: case GOTO_EXPR: + case PREDICT_EXPR: case LABEL_EXPR: case SWITCH_EXPR: - case OMP_RETURN_EXPR: - break; - - case OMP_PARALLEL: + case CHANGE_DYNAMIC_TYPE_EXPR: case OMP_FOR: case OMP_SECTIONS: + case OMP_SECTIONS_SWITCH: case OMP_SECTION: case OMP_SINGLE: case OMP_MASTER: case OMP_ORDERED: case OMP_CRITICAL: + case OMP_RETURN: + case OMP_ATOMIC_LOAD: + case OMP_ATOMIC_STORE: + case OMP_CONTINUE: + break; + + case GIMPLE_MODIFY_STMT: + if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR) + stmt = GIMPLE_STMT_OPERAND (stmt, 1); + else + break; + /* FALLTHRU */ + + case CALL_EXPR: + { + tree decl = get_callee_fndecl (stmt); + if (decl + && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (decl) == BUILT_IN_SETJMP) + { + data->calls_builtin_setjmp = true; + lower_builtin_setjmp (tsi); + return; + } + } + break; + + case OMP_PARALLEL: lower_omp_directive (tsi, data); return; @@ -236,7 +287,7 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) tsi_next (tsi); } -/* Lowers a bind_expr TSI. DATA is passed through the recursion. */ +/* Lower a bind_expr TSI. DATA is passed through the recursion. */ static void lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data) @@ -294,7 +345,7 @@ lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data) This is a subroutine of block_may_fallthru. */ static bool -try_catch_may_fallthru (tree stmt) +try_catch_may_fallthru (const_tree stmt) { tree_stmt_iterator i; @@ -344,9 +395,11 @@ try_catch_may_fallthru (tree stmt) If we're wrong, we'll just delete the extra code later. */ bool -block_may_fallthru (tree block) +block_may_fallthru (const_tree block) { - tree stmt = expr_last (block); + /* This CONST_CAST is okay because expr_last returns it's argument + unmodified and we assign it to a const_tree. */ + const_tree stmt = expr_last (CONST_CAST_TREE(block)); switch (stmt ? TREE_CODE (stmt) : ERROR_MARK) { @@ -386,9 +439,9 @@ block_may_fallthru (tree block) return (block_may_fallthru (TREE_OPERAND (stmt, 0)) && block_may_fallthru (TREE_OPERAND (stmt, 1))); - case MODIFY_EXPR: - if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR) - stmt = TREE_OPERAND (stmt, 1); + case GIMPLE_MODIFY_STMT: + if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR) + stmt = GIMPLE_STMT_OPERAND (stmt, 1); else return true; /* FALLTHRU */ @@ -405,7 +458,7 @@ block_may_fallthru (tree block) } } -/* Lowers a cond_expr TSI. DATA is passed through the recursion. */ +/* Lower a cond_expr TSI. DATA is passed through the recursion. */ static void lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data) @@ -500,6 +553,8 @@ lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data) tsi_next (tsi); } +/* Lower a return_expr TSI. DATA is passed through the recursion. */ + static void lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) { @@ -508,15 +563,15 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) /* Extract the value being returned. */ value = TREE_OPERAND (stmt, 0); - if (value && TREE_CODE (value) == MODIFY_EXPR) - value = TREE_OPERAND (value, 1); + if (value && TREE_CODE (value) == GIMPLE_MODIFY_STMT) + value = GIMPLE_STMT_OPERAND (value, 1); /* Match this up with an existing return statement that's been created. */ for (t = data->return_statements; t ; t = TREE_CHAIN (t)) { tree tvalue = TREE_OPERAND (TREE_VALUE (t), 0); - if (tvalue && TREE_CODE (tvalue) == MODIFY_EXPR) - tvalue = TREE_OPERAND (tvalue, 1); + if (tvalue && TREE_CODE (tvalue) == GIMPLE_MODIFY_STMT) + tvalue = GIMPLE_STMT_OPERAND (tvalue, 1); if (value == tvalue) { @@ -536,6 +591,127 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) tsi_link_before (tsi, t, TSI_SAME_STMT); tsi_delink (tsi); } + +/* Lower a __builtin_setjmp TSI. + + __builtin_setjmp is passed a pointer to an array of five words (not + all will be used on all machines). It operates similarly to the C + library function of the same name, but is more efficient. + + It is lowered into 3 other builtins, namely __builtin_setjmp_setup, + __builtin_setjmp_dispatcher and __builtin_setjmp_receiver, but with + __builtin_setjmp_dispatcher shared among all the instances; that's + why it is only emitted at the end by lower_function_body. + + After full lowering, the body of the function should look like: + + { + void * setjmpvar.0; + int D.1844; + int D.2844; + + [...] + + __builtin_setjmp_setup (&buf, &); + D.1844 = 0; + goto ; + :; + __builtin_setjmp_receiver (&); + D.1844 = 1; + :; + if (D.1844 == 0) goto ; else goto ; + + [...] + + __builtin_setjmp_setup (&buf, &); + D.2844 = 0; + goto ; + :; + __builtin_setjmp_receiver (&); + D.2844 = 1; + :; + if (D.2844 == 0) goto ; else goto ; + + [...] + + :; + return; + : [non-local]; + setjmpvar.0 = __builtin_setjmp_dispatcher (&); + goto setjmpvar.0; + } + + The dispatcher block will be both the unique destination of all the + abnormal call edges and the unique source of all the abnormal edges + to the receivers, thus keeping the complexity explosion localized. */ + +static void +lower_builtin_setjmp (tree_stmt_iterator *tsi) +{ + tree stmt = tsi_stmt (*tsi); + tree cont_label = create_artificial_label (); + tree next_label = create_artificial_label (); + tree dest, t, arg; + + /* NEXT_LABEL is the label __builtin_longjmp will jump to. Its address is + passed to both __builtin_setjmp_setup and __builtin_setjmp_receiver. */ + FORCED_LABEL (next_label) = 1; + + if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT) + { + dest = GIMPLE_STMT_OPERAND (stmt, 0); + stmt = GIMPLE_STMT_OPERAND (stmt, 1); + } + else + dest = NULL_TREE; + + /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert. */ + arg = build_addr (next_label, current_function_decl); + t = implicit_built_in_decls[BUILT_IN_SETJMP_SETUP]; + t = build_call_expr (t, 2, CALL_EXPR_ARG (stmt, 0), arg); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build 'DEST = 0' and insert. */ + if (dest) + { + t = build_gimple_modify_stmt (dest, fold_convert (TREE_TYPE (dest), + integer_zero_node)); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + } + + /* Build 'goto CONT_LABEL' and insert. */ + t = build1 (GOTO_EXPR, void_type_node, cont_label); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build 'NEXT_LABEL:' and insert. */ + t = build1 (LABEL_EXPR, void_type_node, next_label); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build '__builtin_setjmp_receiver (NEXT_LABEL)' and insert. */ + arg = build_addr (next_label, current_function_decl); + t = implicit_built_in_decls[BUILT_IN_SETJMP_RECEIVER]; + t = build_call_expr (t, 1, arg); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build 'DEST = 1' and insert. */ + if (dest) + { + t = build_gimple_modify_stmt (dest, fold_convert (TREE_TYPE (dest), + integer_one_node)); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + } + + /* Build 'CONT_LABEL:' and insert. */ + t = build1 (LABEL_EXPR, void_type_node, cont_label); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Remove the call to __builtin_setjmp. */ + tsi_delink (tsi); +} /* Record the variables in VARS into function FN. */ @@ -543,10 +719,8 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) void record_vars_into (tree vars, tree fn) { - struct function *saved_cfun = cfun; - if (fn != current_function_decl) - cfun = DECL_STRUCT_FUNCTION (fn); + push_cfun (DECL_STRUCT_FUNCTION (fn)); for (; vars; vars = TREE_CHAIN (vars)) { @@ -562,12 +736,12 @@ record_vars_into (tree vars, tree fn) continue; /* Record the variable. */ - cfun->unexpanded_var_list = tree_cons (NULL_TREE, var, - cfun->unexpanded_var_list); + cfun->local_decls = tree_cons (NULL_TREE, var, + cfun->local_decls); } if (fn != current_function_decl) - cfun = saved_cfun; + pop_cfun (); } @@ -610,15 +784,18 @@ mark_blocks_with_used_vars (tree block) /* Mark the used attribute on blocks correctly. */ -static void +static unsigned int mark_used_blocks (void) { mark_blocks_with_used_vars (DECL_INITIAL (current_function_decl)); + return 0; } -struct tree_opt_pass pass_mark_used_blocks = +struct gimple_opt_pass pass_mark_used_blocks = { + { + GIMPLE_PASS, "blocks", /* name */ NULL, /* gate */ mark_used_blocks, /* execute */ @@ -630,6 +807,6 @@ struct tree_opt_pass pass_mark_used_blocks = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ + TODO_dump_func /* todo_flags_finish */ + } };