In Ada, we have issues with vectorization when full checks are enabled, most notably -gnato. This patch makes it possible to vectorize more loops at -O3.
Tested on i586-suse-linux, applied on the mainline. 2012-05-06 Eric Botcazou <ebotca...@adacore.com> * gcc-interface/trans.c (Loop_Statement_to_gnu): Also handle invariant conditions with only one bound. (Raise_Error_to_gnu): Likewise. New function extracted from... (gnat_to_gnu) <N_Raise_Constraint_Error>: ...here. Call above function in regular mode only. -- Eric Botcazou
Index: gcc-interface/trans.c =================================================================== --- gcc-interface/trans.c (revision 187206) +++ gcc-interface/trans.c (working copy) @@ -2563,13 +2563,19 @@ Loop_Statement_to_gnu (Node_Id gnat_node i++) { tree low_ok - = build_binary_op (GE_EXPR, boolean_type_node, - convert (rci->type, gnu_low), - rci->low_bound); + = rci->low_bound + ? build_binary_op (GE_EXPR, boolean_type_node, + convert (rci->type, gnu_low), + rci->low_bound) + : boolean_true_node; + tree high_ok - = build_binary_op (LE_EXPR, boolean_type_node, - convert (rci->type, gnu_high), - rci->high_bound); + = rci->high_bound + ? build_binary_op (LE_EXPR, boolean_type_node, + convert (rci->type, gnu_high), + rci->high_bound) + : boolean_true_node; + tree range_ok = build_binary_op (TRUTH_ANDIF_EXPR, boolean_type_node, low_ok, high_ok); @@ -2794,7 +2800,7 @@ finalize_nrv_r (tree *tp, int *walk_subt tree ret_val = TREE_OPERAND (TREE_OPERAND (t, 0), 1), init_expr; /* If this is the temporary created for a return value with variable - size in call_to_gnu, we replace the RHS with the init expression. */ + size in Call_to_gnu, we replace the RHS with the init expression. */ if (TREE_CODE (ret_val) == COMPOUND_EXPR && TREE_CODE (TREE_OPERAND (ret_val, 0)) == INIT_EXPR && TREE_OPERAND (TREE_OPERAND (ret_val, 0), 0) @@ -3122,7 +3128,7 @@ build_return_expr (tree ret_obj, tree re && aggregate_value_p (operation_type, current_function_decl)) { /* Recognize the temporary created for a return value with variable - size in call_to_gnu. We want to eliminate it if possible. */ + size in Call_to_gnu. We want to eliminate it if possible. */ if (TREE_CODE (ret_val) == COMPOUND_EXPR && TREE_CODE (TREE_OPERAND (ret_val, 0)) == INIT_EXPR && TREE_OPERAND (TREE_OPERAND (ret_val, 0), 0) @@ -3583,7 +3589,7 @@ create_init_temporary (const char *prefi requires atomic synchronization. */ static tree -call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target, +Call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target, bool atomic_sync) { const bool function_call = (Nkind (gnat_node) == N_Function_Call); @@ -4751,6 +4757,134 @@ Compilation_Unit_to_gnu (Node_Id gnat_no invalidate_global_renaming_pointers (); } +/* Subroutine of gnat_to_gnu to translate gnat_node, an N_Raise_xxx_Error, + to a GCC tree, which is returned. GNU_RESULT_TYPE_P is a pointer to where + we should place the result type. LABEL_P is true if there is a label to + branch to for the exception. */ + +static tree +Raise_Error_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p) +{ + const Node_Kind kind = Nkind (gnat_node); + const int reason = UI_To_Int (Reason (gnat_node)); + const Node_Id gnat_cond = Condition (gnat_node); + const bool with_extra_info + = Exception_Extra_Info + && !No_Exception_Handlers_Set () + && !get_exception_label (kind); + tree gnu_result = NULL_TREE, gnu_cond = NULL_TREE; + + *gnu_result_type_p = get_unpadded_type (Etype (gnat_node)); + + switch (reason) + { + case CE_Access_Check_Failed: + if (with_extra_info) + gnu_result = build_call_raise_column (reason, gnat_node); + break; + + case CE_Index_Check_Failed: + case CE_Range_Check_Failed: + case CE_Invalid_Data: + if (Present (gnat_cond) && Nkind (gnat_cond) == N_Op_Not) + { + Node_Id gnat_range, gnat_index, gnat_type; + tree gnu_index, gnu_low_bound, gnu_high_bound; + struct range_check_info_d *rci; + + switch (Nkind (Right_Opnd (gnat_cond))) + { + case N_In: + gnat_range = Right_Opnd (Right_Opnd (gnat_cond)); + gcc_assert (Nkind (gnat_range) == N_Range); + gnu_low_bound = gnat_to_gnu (Low_Bound (gnat_range)); + gnu_high_bound = gnat_to_gnu (High_Bound (gnat_range)); + break; + + case N_Op_Ge: + gnu_low_bound = gnat_to_gnu (Right_Opnd (Right_Opnd (gnat_cond))); + gnu_high_bound = NULL_TREE; + break; + + case N_Op_Le: + gnu_low_bound = NULL_TREE; + gnu_high_bound = gnat_to_gnu (Right_Opnd (Right_Opnd (gnat_cond))); + break; + + default: + goto common; + } + + gnat_index = Left_Opnd (Right_Opnd (gnat_cond)); + gnat_type = Etype (gnat_index); + gnu_index = gnat_to_gnu (gnat_index); + + if (with_extra_info + && gnu_low_bound + && gnu_high_bound + && Known_Esize (gnat_type) + && UI_To_Int (Esize (gnat_type)) <= 32) + gnu_result + = build_call_raise_range (reason, gnat_node, gnu_index, + gnu_low_bound, gnu_high_bound); + + /* If loop unswitching is enabled, we try to compute invariant + conditions for checks applied to iteration variables, i.e. + conditions that are both independent of the variable and + necessary in order for the check to fail in the course of + some iteration, and prepend them to the original condition + of the checks. This will make it possible later for the + loop unswitching pass to replace the loop with two loops, + one of which has the checks eliminated and the other has + the original checks reinstated, and a run time selection. + The former loop will be suitable for vectorization. */ + if (flag_unswitch_loops + && (!gnu_low_bound + || (gnu_low_bound = gnat_invariant_expr (gnu_low_bound))) + && (!gnu_high_bound + || (gnu_high_bound = gnat_invariant_expr (gnu_high_bound))) + && (rci = push_range_check_info (gnu_index))) + { + rci->low_bound = gnu_low_bound; + rci->high_bound = gnu_high_bound; + rci->type = gnat_to_gnu_type (gnat_type); + rci->invariant_cond = build1 (SAVE_EXPR, boolean_type_node, + boolean_true_node); + gnu_cond = build_binary_op (TRUTH_ANDIF_EXPR, + boolean_type_node, + rci->invariant_cond, + gnat_to_gnu (gnat_cond)); + } + } + break; + + default: + break; + } + +common: + if (!gnu_result) + gnu_result = build_call_raise (reason, gnat_node, kind); + set_expr_location_from_node (gnu_result, gnat_node); + + /* If the type is VOID, this is a statement, so we need to generate the code + for the call. Handle a condition, if there is one. */ + if (VOID_TYPE_P (*gnu_result_type_p)) + { + if (Present (gnat_cond)) + { + if (!gnu_cond) + gnu_cond = gnat_to_gnu (gnat_cond); + gnu_result = build3 (COND_EXPR, void_type_node, gnu_cond, gnu_result, + alloc_stmt_list ()); + } + } + else + gnu_result = build1 (NULL_EXPR, *gnu_result_type_p, gnu_result); + + return gnu_result; +} + /* Return true if GNAT_NODE is on the LHS of an assignment or an actual parameter of a call. */ @@ -5949,7 +6083,7 @@ gnat_to_gnu (Node_Id gnat_node) N_Raise_Storage_Error); else if (Nkind (Expression (gnat_node)) == N_Function_Call) gnu_result - = call_to_gnu (Expression (gnat_node), &gnu_result_type, gnu_lhs, + = Call_to_gnu (Expression (gnat_node), &gnu_result_type, gnu_lhs, atomic_sync_required_p (Name (gnat_node))); else { @@ -6238,7 +6372,7 @@ gnat_to_gnu (Node_Id gnat_node) case N_Function_Call: case N_Procedure_Call_Statement: - gnu_result = call_to_gnu (gnat_node, &gnu_result_type, NULL_TREE, false); + gnu_result = Call_to_gnu (gnat_node, &gnu_result_type, NULL_TREE, false); break; /************************/ @@ -6661,105 +6795,10 @@ gnat_to_gnu (Node_Id gnat_node) case N_Raise_Constraint_Error: case N_Raise_Program_Error: case N_Raise_Storage_Error: - { - const int reason = UI_To_Int (Reason (gnat_node)); - const Node_Id gnat_cond = Condition (gnat_node); - const bool with_extra_info = Exception_Extra_Info - && !No_Exception_Handlers_Set () - && !get_exception_label (kind); - tree gnu_cond = NULL_TREE; - - if (type_annotate_only) - { - gnu_result = alloc_stmt_list (); - break; - } - - gnu_result_type = get_unpadded_type (Etype (gnat_node)); - - switch (reason) - { - case CE_Access_Check_Failed: - if (with_extra_info) - gnu_result = build_call_raise_column (reason, gnat_node); - break; - - case CE_Index_Check_Failed: - case CE_Range_Check_Failed: - case CE_Invalid_Data: - if (Present (gnat_cond) - && Nkind (gnat_cond) == N_Op_Not - && Nkind (Right_Opnd (gnat_cond)) == N_In - && Nkind (Right_Opnd (Right_Opnd (gnat_cond))) == N_Range) - { - Node_Id gnat_index = Left_Opnd (Right_Opnd (gnat_cond)); - Node_Id gnat_type = Etype (gnat_index); - Node_Id gnat_range = Right_Opnd (Right_Opnd (gnat_cond)); - tree gnu_index = gnat_to_gnu (gnat_index); - tree gnu_low_bound = gnat_to_gnu (Low_Bound (gnat_range)); - tree gnu_high_bound = gnat_to_gnu (High_Bound (gnat_range)); - struct range_check_info_d *rci; - - if (with_extra_info - && Known_Esize (gnat_type) - && UI_To_Int (Esize (gnat_type)) <= 32) - gnu_result - = build_call_raise_range (reason, gnat_node, gnu_index, - gnu_low_bound, gnu_high_bound); - - /* If loop unswitching is enabled, we try to compute invariant - conditions for checks applied to iteration variables, i.e. - conditions that are both independent of the variable and - necessary in order for the check to fail in the course of - some iteration, and prepend them to the original condition - of the checks. This will make it possible later for the - loop unswitching pass to replace the loop with two loops, - one of which has the checks eliminated and the other has - the original checks reinstated, and a run time selection. - The former loop will be suitable for vectorization. */ - if (flag_unswitch_loops - && (gnu_low_bound = gnat_invariant_expr (gnu_low_bound)) - && (gnu_high_bound = gnat_invariant_expr (gnu_high_bound)) - && (rci = push_range_check_info (gnu_index))) - { - rci->low_bound = gnu_low_bound; - rci->high_bound = gnu_high_bound; - rci->type = gnat_to_gnu_type (gnat_type); - rci->invariant_cond = build1 (SAVE_EXPR, boolean_type_node, - boolean_true_node); - gnu_cond = build_binary_op (TRUTH_ANDIF_EXPR, - boolean_type_node, - rci->invariant_cond, - gnat_to_gnu (gnat_cond)); - } - } - break; - - default: - break; - } - - if (gnu_result == error_mark_node) - gnu_result = build_call_raise (reason, gnat_node, kind); - - set_expr_location_from_node (gnu_result, gnat_node); - - /* If the type is VOID, this is a statement, so we need to generate - the code for the call. Handle a condition, if there is one. */ - if (VOID_TYPE_P (gnu_result_type)) - { - if (Present (gnat_cond)) - { - if (!gnu_cond) - gnu_cond = gnat_to_gnu (gnat_cond); - gnu_result - = build3 (COND_EXPR, void_type_node, gnu_cond, gnu_result, - alloc_stmt_list ()); - } - } - else - gnu_result = build1 (NULL_EXPR, gnu_result_type, gnu_result); - } + if (type_annotate_only) + gnu_result = alloc_stmt_list (); + else + gnu_result = Raise_Error_to_gnu (gnat_node, &gnu_result_type); break; case N_Validate_Unchecked_Conversion: