X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fgimple-low.c;h=fecc667f6b9f4e6776b2d3fcdcd4efc044b4b2fc;hp=b3b6af71ab84d781cfd9455cda9f33c5126b38ca;hb=4514949a22403811d4d4de86cd83a29f2e8693db;hpb=4a6920120beaf2da27fba3e8f1f19361f78d645c diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index b3b6af71ab8..fecc667f6b9 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 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 +16,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, 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" @@ -25,9 +25,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #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" @@ -42,91 +42,167 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "toplev.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 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; - - if (TREE_CODE (bind) != BIND_EXPR) - abort (); - + 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); + 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); /* 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); + gimple_set_location (t.stmt, UNKNOWN_LOCATION); + gsi_insert_after (&i, t.stmt, GSI_CONTINUE_LINKING); } - if (data.block != DECL_INITIAL (current_function_decl)) - abort (); + /* 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; + 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 = implicit_built_in_decls[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)); BLOCK_SUBBLOCKS (data.block) = 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 */ @@ -136,86 +212,213 @@ struct tree_opt_pass pass_lower_cf = 0, /* 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 */ + } }; -/* 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, + mark the call expression so it doesn't get inlined later. */ + +static void +check_call_args (gimple stmt) +{ + tree fndecl, parms, p; + unsigned int i, nargs; + + if (gimple_call_cannot_inline_p (stmt)) + return; + + nargs = gimple_call_num_args (stmt); + + /* Get argument types for verification. */ + fndecl = gimple_call_fndecl (stmt); + parms = NULL_TREE; + if (fndecl) + parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + else if (POINTER_TYPE_P (TREE_TYPE (gimple_call_fn (stmt)))) + parms = TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (gimple_call_fn (stmt)))); + + /* Verify if the type of the argument matches that of the function + declaration. If we cannot verify this or there is a mismatch, + mark the call expression so it doesn't get inlined later. */ + if (fndecl && DECL_ARGUMENTS (fndecl)) + { + for (i = 0, p = DECL_ARGUMENTS (fndecl); + i < nargs; + i++, p = TREE_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))) + { + gimple_call_set_cannot_inline (stmt, true); + break; + } + } + } + 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))) + { + gimple_call_set_cannot_inline (stmt, true); + break; + } + } + } + else + { + if (nargs != 0) + gimple_call_set_cannot_inline (stmt, 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) { - tree_stmt_iterator tsi; + gimple_stmt_iterator gsi; - for (tsi = tsi_start (expr); !tsi_end_p (tsi); ) - lower_stmt (&tsi, data); + for (gsi = gsi_start (seq); !gsi_end_p (gsi); ) + lower_stmt (&gsi, data); } -/* Lowers statement TSI. DATA is passed through the recursion. */ + +/* Lower the OpenMP directive statement pointed by GSI. DATA is + passed through the recursion. */ static void -lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) +lower_omp_directive (gimple_stmt_iterator *gsi, struct lower_data *data) { - tree stmt = tsi_stmt (*tsi); + gimple stmt; + + stmt = gsi_stmt (*gsi); + + 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); +} - if (EXPR_HAS_LOCATION (stmt) && data) - TREE_BLOCK (stmt) = data->block; - switch (TREE_CODE (stmt)) +/* Lower statement GSI. DATA is passed through the recursion. */ + +static void +lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data) +{ + gimple stmt = gsi_stmt (*gsi); + + gimple_set_block (stmt, data->block); + + switch (gimple_code (stmt)) { - case BIND_EXPR: - lower_bind_expr (tsi, data); + case GIMPLE_BIND: + lower_gimple_bind (gsi, data); return; - case COND_EXPR: - lower_cond_expr (tsi, data); - return; - case RETURN_EXPR: - lower_return_expr (tsi, data); + + case GIMPLE_COND: + /* The gimplifier has already lowered this into gotos. */ + break; + + case GIMPLE_RETURN: + lower_gimple_return (gsi, data); 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: + lower_sequence (gimple_try_eval (stmt), data); + lower_sequence (gimple_try_cleanup (stmt), data); break; - case CATCH_EXPR: - lower_stmt_body (CATCH_BODY (stmt), data); + + case GIMPLE_CATCH: + lower_sequence (gimple_catch_handler (stmt), data); break; - case EH_FILTER_EXPR: - lower_stmt_body (EH_FILTER_FAILURE (stmt), data); + + case GIMPLE_EH_FILTER: + 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 VA_ARG_EXPR: - case SWITCH_EXPR: + + case GIMPLE_NOP: + case GIMPLE_ASM: + case GIMPLE_ASSIGN: + case GIMPLE_GOTO: + case GIMPLE_PREDICT: + case GIMPLE_LABEL: + case GIMPLE_SWITCH: + case GIMPLE_CHANGE_DYNAMIC_TYPE: + 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) + { + data->calls_builtin_setjmp = true; + lower_builtin_setjmp (gsi); + return; + } + check_call_args (stmt); + } + break; + + case GIMPLE_OMP_PARALLEL: + case GIMPLE_OMP_TASK: + lower_omp_directive (gsi, data); + return; + default: - print_node_brief (stderr, "", stmt, 0); - case COMPOUND_EXPR: - abort (); + gcc_unreachable (); } - tsi_next (tsi); + 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) { @@ -224,15 +427,13 @@ lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data) /* The outermost block of the original function may not be the outermost statement chain of the gimplified function. So we may see the outermost block just inside the function. */ - if (new_block != DECL_INITIAL (current_function_decl)) - abort (); + gcc_assert (new_block == DECL_INITIAL (current_function_decl)); new_block = NULL; } else { /* We do not expect to handle duplicate blocks. */ - if (TREE_ASM_WRITTEN (new_block)) - abort (); + gcc_assert (!TREE_ASM_WRITTEN (new_block)); TREE_ASM_WRITTEN (new_block) = 1; /* Block tree may get clobbered by inlining. Normally this would @@ -247,44 +448,151 @@ 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) { - if (data->block != new_block) - abort (); + gcc_assert (data->block == new_block); BLOCK_SUBBLOCKS (new_block) = blocks_nreverse (BLOCK_SUBBLOCKS (new_block)); 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_EXPR 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: - case SWITCH_EXPR: /* Easy cases. If the last statement of the block implies control transfer, then we can't fall through. */ return false; + case SWITCH_EXPR: + /* If SWITCH_LABELS is set, this is lowered, and represents a + 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; + case COND_EXPR: if (block_may_fallthru (COND_EXPR_THEN (stmt))) return true; @@ -293,8 +601,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) @@ -306,231 +625,328 @@ block_may_fallthru (tree block) case CALL_EXPR: /* 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 not fall through. */ + return true; - 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); + /* It must be a GIMPLE_TRY_FINALLY. */ - 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); - } + /* 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_ASSIGN: + return true; - if (end_label) - tsi_link_after (tsi, end_label, TSI_CONTINUE_LINKING); + case GIMPLE_CALL: + /* Functions that do not return do not fall through. */ + return (gimple_call_flags (stmt) & ECF_NORETURN) == 0; + + default: + return true; } +} - COND_EXPR_THEN (stmt) = then_goto; - COND_EXPR_ELSE (stmt) = else_goto; - tsi_next (tsi); +/* Same as gimple_stmt_may_fallthru, but for the gimple sequence SEQ. */ + +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) - { - label = TREE_PURPOSE (t); - goto found; - } + if (gimple_return_retval (stmt) == gimple_return_retval (tmp_rs.stmt)) + 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 (); + 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); + 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); +} + +/* 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 (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + tree cont_label = create_artificial_label (); + tree next_label = create_artificial_label (); + 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 = implicit_built_in_decls[BUILT_IN_SETJMP_SETUP]; + g = gimple_build_call (t, 2, gimple_call_arg (stmt, 0), arg); + gimple_set_location (g, gimple_location (stmt)); + 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, fold_convert (TREE_TYPE (dest), + integer_zero_node)); + gimple_set_location (g, gimple_location (stmt)); + gimple_set_block (g, gimple_block (stmt)); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + + /* Build 'goto CONT_LABEL' and insert. */ + g = gimple_build_goto (cont_label); + gsi_insert_before (gsi, g, TSI_SAME_STMT); + + /* Build 'NEXT_LABEL:' and insert. */ + g = gimple_build_label (next_label); + gsi_insert_before (gsi, g, GSI_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]; + g = gimple_build_call (t, 1, arg); + gimple_set_location (g, gimple_location (stmt)); + gimple_set_block (g, gimple_block (stmt)); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Build 'DEST = 1' and insert. */ + if (dest) + { + g = gimple_build_assign (dest, fold_convert (TREE_TYPE (dest), + integer_one_node)); + gimple_set_location (g, gimple_location (stmt)); + 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); } -/* Record the variables in VARS. */ +/* Record the variables in VARS into function FN. */ void -record_vars (tree vars) +record_vars_into (tree vars, tree fn) { + if (fn != current_function_decl) + push_cfun (DECL_STRUCT_FUNCTION (fn)); + for (; vars; vars = TREE_CHAIN (vars)) { tree var = vars; + /* BIND_EXPRs contains also function/type/constant declarations + we don't need to care about. */ + if (TREE_CODE (var) != VAR_DECL) + continue; + /* Nothing to do in this case. */ if (DECL_EXTERNAL (var)) continue; - if (TREE_CODE (var) == FUNCTION_DECL) - 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) + pop_cfun (); } -/* Check whether to expand a variable VAR. */ -static bool -expand_var_p (tree var) -{ - struct var_ann_d *ann; +/* Record the variables in VARS into current_function_decl. */ - if (TREE_CODE (var) != VAR_DECL) - return true; - - /* Remove all unused, unaliased temporaries. Also remove unused, unaliased - local variables during highly optimizing compilations. */ - ann = var_ann (var); - if (ann - && ! ann->may_aliases - && ! ann->used - && ! TREE_ADDRESSABLE (var) - && ! TREE_THIS_VOLATILE (var) - && (DECL_ARTIFICIAL (var) || optimize >= 2)) - return false; - - return true; +void +record_vars (tree vars) +{ + record_vars_into (vars, current_function_decl); } -/* Throw away variables that are unused. */ + +/* Mark BLOCK used if it has a used variable in it, then recurse over its + subblocks. */ static void -remove_useless_vars (void) +mark_blocks_with_used_vars (tree block) { - tree var, *cell; + tree var; + tree subblock; - for (cell = &cfun->unexpanded_var_list; *cell; ) + if (!TREE_USED (block)) { - var = TREE_VALUE (*cell); - - if (!expand_var_p (var)) + for (var = BLOCK_VARS (block); + var; + var = TREE_CHAIN (var)) { - *cell = TREE_CHAIN (*cell); - continue; + if (TREE_USED (var)) + { + TREE_USED (block) = true; + break; + } } - - cell = &TREE_CHAIN (*cell); } + for (subblock = BLOCK_SUBBLOCKS (block); + subblock; + subblock = BLOCK_CHAIN (subblock)) + mark_blocks_with_used_vars (subblock); } -/* Expand variables in the unexpanded_var_list. */ - -void -expand_used_vars (void) -{ - tree cell; - - cfun->unexpanded_var_list = nreverse (cfun->unexpanded_var_list); - - for (cell = cfun->unexpanded_var_list; cell; cell = TREE_CHAIN (cell)) - expand_var (TREE_VALUE (cell)); - - cfun->unexpanded_var_list = NULL_TREE; +/* Mark the used attribute on blocks correctly. */ + +static unsigned int +mark_used_blocks (void) +{ + mark_blocks_with_used_vars (DECL_INITIAL (current_function_decl)); + return 0; } -struct tree_opt_pass pass_remove_useless_vars = + +struct gimple_opt_pass pass_mark_used_blocks = { - "vars", /* name */ + { + GIMPLE_PASS, + "blocks", /* name */ NULL, /* gate */ - remove_useless_vars, /* execute */ + mark_used_blocks, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -540,4 +956,5 @@ struct tree_opt_pass pass_remove_useless_vars = 0, /* properties_destroyed */ 0, /* todo_flags_start */ TODO_dump_func /* todo_flags_finish */ + } };