X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fgimple-low.c;h=f6deba179389d0e3019eb64da162eeb4858783eb;hp=17ba0393c25bda78f8de13e00f740f3e60d995bd;hb=f48068845f391e30be9c0962ab50f70d8c7a4f4d;hpb=7f0f308dcade9eae784cde48b97fcb750195d95e diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index 17ba0393c25..f6deba17938 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -1,12 +1,13 @@ -/* Tree lowering pass. Lowers GIMPLE into unstructured form. +/* GIMPLE lowering pass. Converts High GIMPLE into Low GIMPLE. - Copyright (C) 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + 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,105 +16,167 @@ 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. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "tree.h" -#include "rtl.h" -#include "errors.h" -#include "varray.h" -#include "tree-gimple.h" +#include "gimple.h" +#include "tree-iterator.h" #include "tree-inline.h" -#include "diagnostic.h" -#include "langhooks.h" -#include "langhooks-def.h" #include "tree-flow.h" -#include "timevar.h" -#include "except.h" -#include "hashtab.h" #include "flags.h" #include "function.h" -#include "expr.h" -#include "toplev.h" +#include "diagnostic-core.h" #include "tree-pass.h" +/* The differences between High GIMPLE and Low GIMPLE are the + following: + + 1- Lexical scopes are removed (i.e., GIMPLE_BIND disappears). + + 2- GIMPLE_TRY and GIMPLE_CATCH are converted to abnormal control + flow and exception regions are built as an on-the-side region + hierarchy (See tree-eh.c:lower_eh_constructs). + + 3- Multiple identical return statements are grouped into a single + return and gotos to the unique return site. */ + +/* Match a return statement with a label. During lowering, we identify + identical return statements and replace duplicates with a jump to + the corresponding label. */ +struct return_statements_t +{ + tree label; + gimple stmt; +}; +typedef struct return_statements_t return_statements_t; + +DEF_VEC_O(return_statements_t); +DEF_VEC_ALLOC_O(return_statements_t,heap); + struct lower_data { /* Block the current statement belongs to. */ tree block; - /* A TREE_LIST of label and return statements to be moved to the end + /* A vector of label and return statements to be moved to the end of the function. */ - tree return_statements; + VEC(return_statements_t,heap) *return_statements; + + /* True if the current statement cannot fall through. */ + bool cannot_fallthru; + + /* 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 bool expand_var_p (tree); +static void lower_stmt (gimple_stmt_iterator *, struct lower_data *); +static void lower_gimple_bind (gimple_stmt_iterator *, struct lower_data *); +static void lower_gimple_return (gimple_stmt_iterator *, struct lower_data *); +static void lower_builtin_setjmp (gimple_stmt_iterator *); -/* Lowers the body of current_function_decl. */ -static void +/* Lower the body of current_function_decl from High GIMPLE into Low + GIMPLE. */ + +static unsigned int lower_function_body (void) { struct lower_data data; - tree *body_p = &DECL_SAVED_TREE (current_function_decl); - tree bind = *body_p; - tree_stmt_iterator i; - tree t, x; - - gcc_assert (TREE_CODE (bind) == BIND_EXPR); - + gimple_seq body = gimple_body (current_function_decl); + gimple_seq lowered_body; + gimple_stmt_iterator i; + gimple bind; + tree t; + gimple x; + + /* The gimplifier should've left a body of exactly one statement, + namely a GIMPLE_BIND. */ + gcc_assert (gimple_seq_first (body) == gimple_seq_last (body) + && gimple_code (gimple_seq_first_stmt (body)) == GIMPLE_BIND); + + memset (&data, 0, sizeof (data)); data.block = DECL_INITIAL (current_function_decl); BLOCK_SUBBLOCKS (data.block) = NULL_TREE; BLOCK_CHAIN (data.block) = NULL_TREE; TREE_ASM_WRITTEN (data.block) = 1; + data.return_statements = VEC_alloc (return_statements_t, heap, 8); - data.return_statements = NULL_TREE; + bind = gimple_seq_first_stmt (body); + lowered_body = NULL; + gimple_seq_add_stmt (&lowered_body, bind); + i = gsi_start (lowered_body); + lower_gimple_bind (&i, &data); - *body_p = alloc_stmt_list (); - i = tsi_start (*body_p); - tsi_link_after (&i, bind, TSI_NEW_STMT); - lower_bind_expr (&i, &data); + /* Once the old body has been lowered, replace it with the new + lowered sequence. */ + gimple_set_body (current_function_decl, lowered_body); - i = tsi_last (*body_p); + i = gsi_last (lowered_body); /* If the function falls off the end, we need a null return statement. - If we've already got one in the return_statements list, we don't + If we've already got one in the return_statements vector, we don't need to do anything special. Otherwise build one by hand. */ - if (block_may_fallthru (*body_p) - && (data.return_statements == NULL - || TREE_OPERAND (TREE_VALUE (data.return_statements), 0) != NULL)) + if (gimple_seq_may_fallthru (lowered_body) + && (VEC_empty (return_statements_t, data.return_statements) + || gimple_return_retval (VEC_last (return_statements_t, + data.return_statements)->stmt) != NULL)) { - x = build (RETURN_EXPR, void_type_node, NULL); - SET_EXPR_LOCATION (x, cfun->function_end_locus); - tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + x = gimple_build_return (NULL); + gimple_set_location (x, cfun->function_end_locus); + gimple_set_block (x, DECL_INITIAL (current_function_decl)); + gsi_insert_after (&i, x, GSI_CONTINUE_LINKING); } /* If we lowered any return statements, emit the representative at the end of the function. */ - for (t = data.return_statements ; t ; t = TREE_CHAIN (t)) + while (!VEC_empty (return_statements_t, data.return_statements)) { - x = build (LABEL_EXPR, void_type_node, TREE_PURPOSE (t)); - tsi_link_after (&i, x, TSI_CONTINUE_LINKING); - - /* Remove the line number from the representative return statement. - 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); + return_statements_t t; + + /* Unfortunately, we can't use VEC_pop because it returns void for + objects. */ + t = *VEC_last (return_statements_t, data.return_statements); + VEC_truncate (return_statements_t, + data.return_statements, + VEC_length (return_statements_t, + data.return_statements) - 1); + + x = gimple_build_label (t.label); + gsi_insert_after (&i, x, GSI_CONTINUE_LINKING); + gsi_insert_after (&i, t.stmt, GSI_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 (cfun->function_end_locus); + /* This mark will create forward edges from every call site. */ + DECL_NONLOCAL (disp_label) = 1; + cfun->has_nonlocal_label = 1; + x = gimple_build_label (disp_label); + gsi_insert_after (&i, x, GSI_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 = builtin_decl_implicit (BUILT_IN_SETJMP_DISPATCHER); + x = gimple_build_call (t, 1, arg); + gimple_call_set_lhs (x, disp_var); + + /* Build 'goto DISP_VAR;' and insert. */ + gsi_insert_after (&i, x, GSI_CONTINUE_LINKING); + x = gimple_build_goto (disp_var); + gsi_insert_after (&i, x, GSI_CONTINUE_LINKING); } gcc_assert (data.block == DECL_INITIAL (current_function_decl)); @@ -121,102 +184,293 @@ lower_function_body (void) = blocks_nreverse (BLOCK_SUBBLOCKS (data.block)); clear_block_marks (data.block); + VEC_free(return_statements_t, heap, data.return_statements); + 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 */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - 0, /* tv_id */ + TV_NONE, /* tv_id */ PROP_gimple_any, /* properties_required */ PROP_gimple_lcf, /* properties_provided */ - PROP_gimple_any, /* properties_destroyed */ + 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ + 0 /* todo_flags_finish */ + } }; -/* Lowers the EXPR. Unlike gimplification the statements are not relowered + +/* Verify if the type of the argument matches that of the function + declaration. If we cannot verify this or there is a mismatch, + return false. */ + +static bool +gimple_check_call_args (gimple stmt, tree fndecl) +{ + tree parms, p; + unsigned int i, nargs; + + /* Calls to internal functions always match their signature. */ + if (gimple_call_internal_p (stmt)) + return true; + + nargs = gimple_call_num_args (stmt); + + /* Get argument types for verification. */ + if (fndecl) + parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + else + parms = TYPE_ARG_TYPES (gimple_call_fntype (stmt)); + + /* Verify if the type of the argument matches that of the function + declaration. If we cannot verify this or there is a mismatch, + return false. */ + if (fndecl && DECL_ARGUMENTS (fndecl)) + { + for (i = 0, p = DECL_ARGUMENTS (fndecl); + i < nargs; + i++, p = DECL_CHAIN (p)) + { + /* We cannot distinguish a varargs function from the case + of excess parameters, still deferring the inlining decision + to the callee is possible. */ + if (!p) + break; + if (p == error_mark_node + || gimple_call_arg (stmt, i) == error_mark_node + || !fold_convertible_p (DECL_ARG_TYPE (p), + gimple_call_arg (stmt, i))) + return false; + } + } + else if (parms) + { + for (i = 0, p = parms; i < nargs; i++, p = TREE_CHAIN (p)) + { + /* If this is a varargs function defer inlining decision + to callee. */ + if (!p) + break; + if (TREE_VALUE (p) == error_mark_node + || gimple_call_arg (stmt, i) == error_mark_node + || TREE_CODE (TREE_VALUE (p)) == VOID_TYPE + || !fold_convertible_p (TREE_VALUE (p), + gimple_call_arg (stmt, i))) + return false; + } + } + else + { + if (nargs != 0) + return false; + } + return true; +} + +/* Verify if the type of the argument and lhs of CALL_STMT matches + that of the function declaration CALLEE. + If we cannot verify this or there is a mismatch, return false. */ + +bool +gimple_check_call_matching_types (gimple call_stmt, tree callee) +{ + tree lhs; + + if ((DECL_RESULT (callee) + && !DECL_BY_REFERENCE (DECL_RESULT (callee)) + && (lhs = gimple_call_lhs (call_stmt)) != NULL_TREE + && !useless_type_conversion_p (TREE_TYPE (DECL_RESULT (callee)), + TREE_TYPE (lhs)) + && !fold_convertible_p (TREE_TYPE (DECL_RESULT (callee)), lhs)) + || !gimple_check_call_args (call_stmt, callee)) + return false; + return true; +} + +/* Lower sequence SEQ. 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. */ -void -lower_stmt_body (tree expr, struct lower_data *data) +static void +lower_sequence (gimple_seq seq, struct lower_data *data) +{ + gimple_stmt_iterator gsi; + + for (gsi = gsi_start (seq); !gsi_end_p (gsi); ) + lower_stmt (&gsi, data); +} + + +/* Lower the OpenMP directive statement pointed by GSI. DATA is + passed through the recursion. */ + +static void +lower_omp_directive (gimple_stmt_iterator *gsi, struct lower_data *data) { - tree_stmt_iterator tsi; + gimple stmt; + + stmt = gsi_stmt (*gsi); - for (tsi = tsi_start (expr); !tsi_end_p (tsi); ) - lower_stmt (&tsi, data); + lower_sequence (gimple_omp_body (stmt), data); + gsi_insert_before (gsi, stmt, GSI_SAME_STMT); + gsi_insert_seq_before (gsi, gimple_omp_body (stmt), GSI_SAME_STMT); + gimple_omp_set_body (stmt, NULL); + gsi_remove (gsi, false); } -/* Lowers statement TSI. DATA is passed through the recursion. */ + +/* Lower statement GSI. DATA is passed through the recursion. We try to + track the fallthruness of statements and get rid of unreachable return + statements in order to prevent the EH lowering pass from adding useless + edges that can cause bogus warnings to be issued later; this guess need + not be 100% accurate, simply be conservative and reset cannot_fallthru + to false if we don't know. */ static void -lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) +lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data) { - tree stmt = tsi_stmt (*tsi); + gimple stmt = gsi_stmt (*gsi); - if (EXPR_HAS_LOCATION (stmt) && data) - TREE_BLOCK (stmt) = data->block; + gimple_set_block (stmt, data->block); - switch (TREE_CODE (stmt)) + switch (gimple_code (stmt)) { - case BIND_EXPR: - lower_bind_expr (tsi, data); + case GIMPLE_BIND: + lower_gimple_bind (gsi, data); + /* Propagate fallthruness. */ return; - case COND_EXPR: - lower_cond_expr (tsi, data); + + case GIMPLE_COND: + case GIMPLE_GOTO: + case GIMPLE_SWITCH: + data->cannot_fallthru = true; + gsi_next (gsi); return; - case RETURN_EXPR: - lower_return_expr (tsi, data); + + case GIMPLE_RETURN: + if (data->cannot_fallthru) + { + gsi_remove (gsi, false); + /* Propagate fallthruness. */ + } + else + { + lower_gimple_return (gsi, data); + data->cannot_fallthru = true; + } return; - case TRY_FINALLY_EXPR: - case TRY_CATCH_EXPR: - lower_stmt_body (TREE_OPERAND (stmt, 0), data); - lower_stmt_body (TREE_OPERAND (stmt, 1), data); + case GIMPLE_TRY: + { + bool try_cannot_fallthru; + lower_sequence (gimple_try_eval (stmt), data); + try_cannot_fallthru = data->cannot_fallthru; + data->cannot_fallthru = false; + lower_sequence (gimple_try_cleanup (stmt), data); + /* See gimple_stmt_may_fallthru for the rationale. */ + if (gimple_try_kind (stmt) == GIMPLE_TRY_FINALLY) + { + data->cannot_fallthru |= try_cannot_fallthru; + gsi_next (gsi); + return; + } + } break; - case CATCH_EXPR: - lower_stmt_body (CATCH_BODY (stmt), data); + + case GIMPLE_CATCH: + data->cannot_fallthru = false; + lower_sequence (gimple_catch_handler (stmt), data); break; - case EH_FILTER_EXPR: - lower_stmt_body (EH_FILTER_FAILURE (stmt), data); + + case GIMPLE_EH_FILTER: + data->cannot_fallthru = false; + lower_sequence (gimple_eh_filter_failure (stmt), data); break; - - case NOP_EXPR: - case ASM_EXPR: - case MODIFY_EXPR: - case CALL_EXPR: - case GOTO_EXPR: - case LABEL_EXPR: - case SWITCH_EXPR: + + case GIMPLE_EH_ELSE: + lower_sequence (gimple_eh_else_n_body (stmt), data); + lower_sequence (gimple_eh_else_e_body (stmt), data); + break; + + case GIMPLE_NOP: + case GIMPLE_ASM: + case GIMPLE_ASSIGN: + case GIMPLE_PREDICT: + case GIMPLE_LABEL: + case GIMPLE_EH_MUST_NOT_THROW: + case GIMPLE_OMP_FOR: + case GIMPLE_OMP_SECTIONS: + case GIMPLE_OMP_SECTIONS_SWITCH: + case GIMPLE_OMP_SECTION: + case GIMPLE_OMP_SINGLE: + case GIMPLE_OMP_MASTER: + case GIMPLE_OMP_ORDERED: + case GIMPLE_OMP_CRITICAL: + case GIMPLE_OMP_RETURN: + case GIMPLE_OMP_ATOMIC_LOAD: + case GIMPLE_OMP_ATOMIC_STORE: + case GIMPLE_OMP_CONTINUE: + break; + + case GIMPLE_CALL: + { + tree decl = gimple_call_fndecl (stmt); + + if (decl + && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (decl) == BUILT_IN_SETJMP) + { + lower_builtin_setjmp (gsi); + data->cannot_fallthru = false; + data->calls_builtin_setjmp = true; + return; + } + + if (decl && (flags_from_decl_or_type (decl) & ECF_NORETURN)) + { + data->cannot_fallthru = true; + gsi_next (gsi); + return; + } + } + break; + + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TASK: + data->cannot_fallthru = false; + lower_omp_directive (gsi, data); + data->cannot_fallthru = false; + return; + + case GIMPLE_TRANSACTION: + lower_sequence (gimple_transaction_body (stmt), data); break; default: -#ifdef ENABLE_CHECKING - print_node_brief (stderr, "", stmt, 0); - internal_error ("unexpected node"); -#endif - case COMPOUND_EXPR: gcc_unreachable (); } - tsi_next (tsi); + data->cannot_fallthru = false; + gsi_next (gsi); } -/* 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) +lower_gimple_bind (gimple_stmt_iterator *gsi, struct lower_data *data) { tree old_block = data->block; - tree stmt = tsi_stmt (*tsi); - tree new_block = BIND_EXPR_BLOCK (stmt); + gimple stmt = gsi_stmt (*gsi); + tree new_block = gimple_bind_block (stmt); if (new_block) { @@ -246,8 +500,8 @@ lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data) } } - record_vars (BIND_EXPR_VARS (stmt)); - lower_stmt_body (BIND_EXPR_BODY (stmt), data); + record_vars (gimple_bind_vars (stmt)); + lower_sequence (gimple_bind_body (stmt), data); if (new_block) { @@ -258,27 +512,128 @@ lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data) data->block = old_block; } - /* The BIND_EXPR no longer carries any useful information -- kill it. */ - tsi_link_before (tsi, BIND_EXPR_BODY (stmt), TSI_SAME_STMT); - tsi_delink (tsi); + /* The GIMPLE_BIND no longer carries any useful information -- kill it. */ + gsi_insert_seq_before (gsi, gimple_bind_body (stmt), GSI_SAME_STMT); + gsi_remove (gsi, false); +} + +/* Try to determine whether a TRY_CATCH expression can fall through. + This is a subroutine of block_may_fallthru. */ + +static bool +try_catch_may_fallthru (const_tree stmt) +{ + tree_stmt_iterator i; + + /* If the TRY block can fall through, the whole TRY_CATCH can + fall through. */ + if (block_may_fallthru (TREE_OPERAND (stmt, 0))) + return true; + + i = tsi_start (TREE_OPERAND (stmt, 1)); + switch (TREE_CODE (tsi_stmt (i))) + { + case CATCH_EXPR: + /* We expect to see a sequence of CATCH_EXPR trees, each with a + catch expression and a body. The whole TRY_CATCH may fall + through iff any of the catch bodies falls through. */ + for (; !tsi_end_p (i); tsi_next (&i)) + { + if (block_may_fallthru (CATCH_BODY (tsi_stmt (i)))) + return true; + } + return false; + + case EH_FILTER_EXPR: + /* The exception filter expression only matters if there is an + exception. If the exception does not match EH_FILTER_TYPES, + we will execute EH_FILTER_FAILURE, and we will fall through + if that falls through. If the exception does match + EH_FILTER_TYPES, the stack unwinder will continue up the + stack, so we will not fall through. We don't know whether we + will throw an exception which matches EH_FILTER_TYPES or not, + so we just ignore EH_FILTER_TYPES and assume that we might + throw an exception which doesn't match. */ + return block_may_fallthru (EH_FILTER_FAILURE (tsi_stmt (i))); + + default: + /* This case represents statements to be executed when an + exception occurs. Those statements are implicitly followed + by a RESX statement to resume execution after the exception. + So in this case the TRY_CATCH never falls through. */ + return false; + } +} + + +/* Same as above, but for a GIMPLE_TRY_CATCH. */ + +static bool +gimple_try_catch_may_fallthru (gimple stmt) +{ + gimple_stmt_iterator i; + + /* We don't handle GIMPLE_TRY_FINALLY. */ + gcc_assert (gimple_try_kind (stmt) == GIMPLE_TRY_CATCH); + + /* If the TRY block can fall through, the whole TRY_CATCH can + fall through. */ + if (gimple_seq_may_fallthru (gimple_try_eval (stmt))) + return true; + + i = gsi_start (gimple_try_cleanup (stmt)); + switch (gimple_code (gsi_stmt (i))) + { + case GIMPLE_CATCH: + /* We expect to see a sequence of GIMPLE_CATCH stmts, each with a + catch expression and a body. The whole try/catch may fall + through iff any of the catch bodies falls through. */ + for (; !gsi_end_p (i); gsi_next (&i)) + { + if (gimple_seq_may_fallthru (gimple_catch_handler (gsi_stmt (i)))) + return true; + } + return false; + + case GIMPLE_EH_FILTER: + /* The exception filter expression only matters if there is an + exception. If the exception does not match EH_FILTER_TYPES, + we will execute EH_FILTER_FAILURE, and we will fall through + if that falls through. If the exception does match + EH_FILTER_TYPES, the stack unwinder will continue up the + stack, so we will not fall through. We don't know whether we + will throw an exception which matches EH_FILTER_TYPES or not, + so we just ignore EH_FILTER_TYPES and assume that we might + throw an exception which doesn't match. */ + return gimple_seq_may_fallthru (gimple_eh_filter_failure (gsi_stmt (i))); + + default: + /* This case represents statements to be executed when an + exception occurs. Those statements are implicitly followed + by a GIMPLE_RESX to resume execution after the exception. So + in this case the try/catch never falls through. */ + return false; + } } + /* Try to determine if we can fall out of the bottom of BLOCK. This guess need not be 100% accurate; simply be conservative and return true if we don't know. This is used only to avoid stupidly generating extra code. 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 its 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) { case GOTO_EXPR: case RETURN_EXPR: - case RESX_EXPR: - /* Easy cases. If the last statement of the block implies + /* Easy cases. If the last statement of the block implies control transfer, then we can't fall through. */ return false; @@ -287,7 +642,7 @@ block_may_fallthru (tree block) branch to a selected label and hence can not fall through. Otherwise SWITCH_BODY is set, and the switch can fall through. */ - return SWITCH_LABELS (stmt) != NULL_TREE; + return SWITCH_LABELS (stmt) == NULL_TREE; case COND_EXPR: if (block_may_fallthru (COND_EXPR_THEN (stmt))) @@ -297,8 +652,19 @@ block_may_fallthru (tree block) case BIND_EXPR: return block_may_fallthru (BIND_EXPR_BODY (stmt)); + case TRY_CATCH_EXPR: + return try_catch_may_fallthru (stmt); + case TRY_FINALLY_EXPR: - return block_may_fallthru (TREE_OPERAND (stmt, 1)); + /* The finally clause is always executed after the try clause, + so if it does not fall through, then the try-finally will not + fall through. Otherwise, if the try clause does not fall + through, then when the finally clause falls through it will + resume execution wherever the try clause was going. So the + whole try-finally will only fall through if both the try + clause and the finally clause fall through. */ + 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) @@ -311,292 +677,289 @@ block_may_fallthru (tree block) /* Functions that do not return do not fall through. */ return (call_expr_flags (stmt) & ECF_NORETURN) == 0; + case CLEANUP_POINT_EXPR: + return block_may_fallthru (TREE_OPERAND (stmt, 0)); + default: return true; } } -/* Lowers a cond_expr TSI. DATA is passed through the recursion. */ -static void -lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data) +/* Try to determine if we can continue executing the statement + immediately following STMT. This guess need not be 100% accurate; + simply be conservative and return true if we don't know. This is + used only to avoid stupidly generating extra code. If we're wrong, + we'll just delete the extra code later. */ + +bool +gimple_stmt_may_fallthru (gimple stmt) { - tree stmt = tsi_stmt (*tsi); - bool then_is_goto, else_is_goto; - tree then_branch, else_branch; - tree then_goto, else_goto; - - then_branch = COND_EXPR_THEN (stmt); - else_branch = COND_EXPR_ELSE (stmt); + if (!stmt) + return true; - lower_stmt_body (then_branch, data); - lower_stmt_body (else_branch, data); + switch (gimple_code (stmt)) + { + case GIMPLE_GOTO: + case GIMPLE_RETURN: + case GIMPLE_RESX: + /* Easy cases. If the last statement of the seq implies + control transfer, then we can't fall through. */ + return false; - then_goto = expr_only (then_branch); - then_is_goto = then_goto && simple_goto_p (then_goto); + case GIMPLE_SWITCH: + /* Switch has already been lowered and represents a branch + to a selected label and hence can't fall through. */ + return false; - else_goto = expr_only (else_branch); - else_is_goto = else_goto && simple_goto_p (else_goto); + case GIMPLE_COND: + /* GIMPLE_COND's are already lowered into a two-way branch. They + can't fall through. */ + return false; - if (!then_is_goto || !else_is_goto) - { - tree then_label, else_label, end_label, t; - - then_label = NULL_TREE; - else_label = NULL_TREE; - end_label = NULL_TREE; - - /* Replace the cond_expr with explicit gotos. */ - if (!then_is_goto) - { - t = build1 (LABEL_EXPR, void_type_node, NULL_TREE); - if (TREE_SIDE_EFFECTS (then_branch)) - then_label = t; - else - end_label = t; - then_goto = build_and_jump (&LABEL_EXPR_LABEL (t)); - } + case GIMPLE_BIND: + return gimple_seq_may_fallthru (gimple_bind_body (stmt)); - if (!else_is_goto) - { - t = build1 (LABEL_EXPR, void_type_node, NULL_TREE); - if (TREE_SIDE_EFFECTS (else_branch)) - else_label = t; - else - { - /* Both THEN and ELSE can be no-ops if one or both contained an - empty BIND_EXPR that was associated with the toplevel block - of an inlined function. In that case remove_useless_stmts - can't have cleaned things up for us; kill the whole - conditional now. */ - if (end_label) - { - tsi_delink (tsi); - return; - } - else - end_label = t; - } - else_goto = build_and_jump (&LABEL_EXPR_LABEL (t)); - } + case GIMPLE_TRY: + if (gimple_try_kind (stmt) == GIMPLE_TRY_CATCH) + return gimple_try_catch_may_fallthru (stmt); - if (then_label) - { - bool may_fallthru = block_may_fallthru (then_branch); - - tsi_link_after (tsi, then_label, TSI_CONTINUE_LINKING); - tsi_link_after (tsi, then_branch, TSI_CONTINUE_LINKING); - - if (else_label && may_fallthru) - { - end_label = build1 (LABEL_EXPR, void_type_node, NULL_TREE); - t = build_and_jump (&LABEL_EXPR_LABEL (end_label)); - tsi_link_after (tsi, t, TSI_CONTINUE_LINKING); - } - } - - if (else_label) - { - tsi_link_after (tsi, else_label, TSI_CONTINUE_LINKING); - tsi_link_after (tsi, else_branch, TSI_CONTINUE_LINKING); - } + /* It must be a GIMPLE_TRY_FINALLY. */ + + /* The finally clause is always executed after the try clause, + so if it does not fall through, then the try-finally will not + fall through. Otherwise, if the try clause does not fall + through, then when the finally clause falls through it will + resume execution wherever the try clause was going. So the + whole try-finally will only fall through if both the try + clause and the finally clause fall through. */ + return (gimple_seq_may_fallthru (gimple_try_eval (stmt)) + && gimple_seq_may_fallthru (gimple_try_cleanup (stmt))); + + case GIMPLE_EH_ELSE: + return (gimple_seq_may_fallthru (gimple_eh_else_n_body (stmt)) + || gimple_seq_may_fallthru (gimple_eh_else_e_body (stmt))); + + case GIMPLE_CALL: + /* Functions that do not return do not fall through. */ + return (gimple_call_flags (stmt) & ECF_NORETURN) == 0; - if (end_label) - tsi_link_after (tsi, end_label, TSI_CONTINUE_LINKING); + default: + return true; } +} + - COND_EXPR_THEN (stmt) = then_goto; - COND_EXPR_ELSE (stmt) = else_goto; +/* Same as gimple_stmt_may_fallthru, but for the gimple sequence SEQ. */ - tsi_next (tsi); +bool +gimple_seq_may_fallthru (gimple_seq seq) +{ + return gimple_stmt_may_fallthru (gimple_seq_last_stmt (seq)); } + +/* Lower a GIMPLE_RETURN GSI. DATA is passed through the recursion. */ + static void -lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) +lower_gimple_return (gimple_stmt_iterator *gsi, struct lower_data *data) { - tree stmt = tsi_stmt (*tsi); - tree value, t, label; - - /* Extract the value being returned. */ - value = TREE_OPERAND (stmt, 0); - if (value && TREE_CODE (value) == MODIFY_EXPR) - value = TREE_OPERAND (value, 1); + gimple stmt = gsi_stmt (*gsi); + gimple t; + int i; + return_statements_t tmp_rs; /* Match this up with an existing return statement that's been created. */ - for (t = data->return_statements; t ; t = TREE_CHAIN (t)) + for (i = VEC_length (return_statements_t, data->return_statements) - 1; + i >= 0; i--) { - tree tvalue = TREE_OPERAND (TREE_VALUE (t), 0); - if (tvalue && TREE_CODE (tvalue) == MODIFY_EXPR) - tvalue = TREE_OPERAND (tvalue, 1); + tmp_rs = *VEC_index (return_statements_t, data->return_statements, i); - if (value == tvalue) + if (gimple_return_retval (stmt) == gimple_return_retval (tmp_rs.stmt)) { - label = TREE_PURPOSE (t); + /* Remove the line number from the representative return statement. + It now fills in for many such returns. Failure to remove this + will result in incorrect results for coverage analysis. */ + gimple_set_location (tmp_rs.stmt, UNKNOWN_LOCATION); + goto found; } } /* Not found. Create a new label and record the return statement. */ - label = create_artificial_label (); - data->return_statements = tree_cons (label, stmt, data->return_statements); + tmp_rs.label = create_artificial_label (cfun->function_end_locus); + tmp_rs.stmt = stmt; + VEC_safe_push (return_statements_t, heap, data->return_statements, &tmp_rs); /* Generate a goto statement and remove the return statement. */ found: - t = build (GOTO_EXPR, void_type_node, label); - SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); - tsi_link_before (tsi, t, TSI_SAME_STMT); - tsi_delink (tsi); + /* When not optimizing, make sure user returns are preserved. */ + if (!optimize && gimple_has_location (stmt)) + DECL_ARTIFICIAL (tmp_rs.label) = 0; + t = gimple_build_goto (tmp_rs.label); + gimple_set_location (t, gimple_location (stmt)); + gimple_set_block (t, gimple_block (stmt)); + gsi_insert_before (gsi, t, GSI_SAME_STMT); + gsi_remove (gsi, false); } - -/* Record the variables in VARS. */ +/* Lower a __builtin_setjmp GSI. -void -record_vars (tree vars) -{ - for (; vars; vars = TREE_CHAIN (vars)) - { - tree var = vars; + __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. - /* Nothing to do in this case. */ - if (DECL_EXTERNAL (var)) - continue; - if (TREE_CODE (var) == FUNCTION_DECL) - continue; + 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. - /* Record the variable. */ - cfun->unexpanded_var_list = tree_cons (NULL_TREE, var, - cfun->unexpanded_var_list); + 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; } -} -/* Check whether to expand a variable VAR. */ + 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 bool -expand_var_p (tree var) +static void +lower_builtin_setjmp (gimple_stmt_iterator *gsi) { - struct var_ann_d *ann; + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + tree cont_label = create_artificial_label (loc); + tree next_label = create_artificial_label (loc); + tree dest, t, arg; + gimple g; + + /* 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; + + dest = gimple_call_lhs (stmt); + + /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert. */ + arg = build_addr (next_label, current_function_decl); + t = builtin_decl_implicit (BUILT_IN_SETJMP_SETUP); + g = gimple_build_call (t, 2, gimple_call_arg (stmt, 0), arg); + gimple_set_location (g, loc); + gimple_set_block (g, gimple_block (stmt)); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Build 'DEST = 0' and insert. */ + if (dest) + { + g = gimple_build_assign (dest, build_zero_cst (TREE_TYPE (dest))); + gimple_set_location (g, loc); + gimple_set_block (g, gimple_block (stmt)); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } - if (TREE_CODE (var) != VAR_DECL) - return true; + /* Build 'goto CONT_LABEL' and insert. */ + g = gimple_build_goto (cont_label); + gsi_insert_before (gsi, g, GSI_SAME_STMT); - /* Leave statics and externals alone. */ - if (TREE_STATIC (var) || DECL_EXTERNAL (var)) - return true; + /* Build 'NEXT_LABEL:' and insert. */ + g = gimple_build_label (next_label); + gsi_insert_before (gsi, g, GSI_SAME_STMT); - /* Remove all unused local variables. */ - ann = var_ann (var); - if (!ann || !ann->used) - return false; + /* Build '__builtin_setjmp_receiver (NEXT_LABEL)' and insert. */ + arg = build_addr (next_label, current_function_decl); + t = builtin_decl_implicit (BUILT_IN_SETJMP_RECEIVER); + g = gimple_build_call (t, 1, arg); + gimple_set_location (g, loc); + gimple_set_block (g, gimple_block (stmt)); + gsi_insert_before (gsi, g, GSI_SAME_STMT); - return true; + /* Build 'DEST = 1' and insert. */ + if (dest) + { + g = gimple_build_assign (dest, fold_convert_loc (loc, TREE_TYPE (dest), + integer_one_node)); + gimple_set_location (g, loc); + gimple_set_block (g, gimple_block (stmt)); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + + /* Build 'CONT_LABEL:' and insert. */ + g = gimple_build_label (cont_label); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Remove the call to __builtin_setjmp. */ + gsi_remove (gsi, false); } + -/* Throw away variables that are unused. */ +/* Record the variables in VARS into function FN. */ -static void -remove_useless_vars (void) +void +record_vars_into (tree vars, tree fn) { - tree var, *cell; - FILE *df = NULL; + if (fn != current_function_decl) + push_cfun (DECL_STRUCT_FUNCTION (fn)); - if (dump_file && (dump_flags & TDF_DETAILS)) + for (; vars; vars = DECL_CHAIN (vars)) { - df = dump_file; - fputs ("Discarding as unused:\n", df); - } + tree var = vars; - for (cell = &cfun->unexpanded_var_list; *cell; ) - { - var = TREE_VALUE (*cell); + /* BIND_EXPRs contains also function/type/constant declarations + we don't need to care about. */ + if (TREE_CODE (var) != VAR_DECL) + continue; - if (!expand_var_p (var)) - { - if (df) - { - fputs (" ", df); - print_generic_expr (df, var, dump_flags); - fputc ('\n', df); - } - - *cell = TREE_CHAIN (*cell); - continue; - } + /* Nothing to do in this case. */ + if (DECL_EXTERNAL (var)) + continue; - cell = &TREE_CHAIN (*cell); + /* Record the variable. */ + add_local_decl (cfun, var); + if (gimple_referenced_vars (cfun)) + add_referenced_var (var); } - if (df) - fputc ('\n', df); + if (fn != current_function_decl) + pop_cfun (); } -struct tree_opt_pass pass_remove_useless_vars = -{ - "vars", /* name */ - NULL, /* gate */ - remove_useless_vars, /* 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 */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ -}; -/* Mark BLOCK used if it has a used variable in it, then recurse over its - subblocks. */ +/* Record the variables in VARS into current_function_decl. */ -static void -mark_blocks_with_used_vars (tree block) +void +record_vars (tree vars) { - tree var; - tree subblock; - - if (!TREE_USED (block)) - { - for (var = BLOCK_VARS (block); - var; - var = TREE_CHAIN (var)) - { - if (TREE_USED (var)) - { - TREE_USED (block) = true; - break; - } - } - } - for (subblock = BLOCK_SUBBLOCKS (block); - subblock; - subblock = BLOCK_CHAIN (subblock)) - mark_blocks_with_used_vars (subblock); -} - -/* Mark the used attribute on blocks correctly. */ - -static void -mark_used_blocks (void) -{ - mark_blocks_with_used_vars (DECL_INITIAL (current_function_decl)); + record_vars_into (vars, current_function_decl); } - - -struct tree_opt_pass pass_mark_used_blocks = -{ - "blocks", /* name */ - NULL, /* gate */ - mark_used_blocks, /* 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 */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ -};