diff --git a/gcc/ChangeLog b/gcc/ChangeLog index da8bbf58caf..038da597b41 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2007-01-22 Richard Guenther + + PR tree-optimization/30038 + * tree-ssa-math-opts.c (maybe_record_sincos): New static helper + function. + (execute_cse_sincos_1): Likewise. + (execute_cse_sincos): Likewise. + (gate_cse_sincos): Likewise. + (pass_cse_sincos): New pass CSEing sin() and cos() calls using + the cexpi() canonicalization of sincos(). + * tree-pass.h (pass_cse_sincos): Declare. + * passes.c (init_optimization_passes): New pass pas_cse_sincos. + 2007-01-21 Eric Botcazou PR rtl-optimization/29329 diff --git a/gcc/passes.c b/gcc/passes.c index 0ab5f4815e4..2e17599bc8c 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -573,6 +573,7 @@ init_optimization_passes (void) NEXT_PASS (pass_store_ccp); NEXT_PASS (pass_store_copy_prop); NEXT_PASS (pass_fold_builtins); + NEXT_PASS (pass_cse_sincos); /* FIXME: May alias should a TODO but for 4.0.0, we add may_alias right after fold builtins which can create arbitrary GIMPLE. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ff69cda1751..aef566ae1d4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2007-01-22 Richard Guenther + + PR tree-optimization/30038 + * gcc.dg/builtins-62.c: New testcase. + 2007-01-21 Eric Botcazou * gcc.c-torture/compile/20070121.c: New test. diff --git a/gcc/testsuite/gcc.dg/builtins-62.c b/gcc/testsuite/gcc.dg/builtins-62.c new file mode 100644 index 00000000000..af7a02a4c07 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtins-62.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-O -ffinite-math-only -fdump-tree-optimized" } */ + +double test1 (double x) +{ + double s, c; + s = __builtin_sin (x); + c = __builtin_cos (x); + return s + c; +} + +double test2 (double x) +{ + double s, c; + x = x * 2; + s = __builtin_sin (x); + c = __builtin_cos (x); + return s + c; +} + +double test3 (double x, int b) +{ + double s, c; + if (b) + x = x * 2; + s = __builtin_sin (x); + c = __builtin_cos (x); + return s + c; +} + +double test4 (double x) +{ + double s; + x = x * 2; + s = __builtin_sin (x); + return s; +} + +/* { dg-final { scan-tree-dump-times "cexpi" 3 "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index b904e10d624..8556eb771c9 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -282,6 +282,7 @@ extern struct tree_opt_pass pass_stdarg; extern struct tree_opt_pass pass_early_warn_uninitialized; extern struct tree_opt_pass pass_late_warn_uninitialized; extern struct tree_opt_pass pass_cse_reciprocals; +extern struct tree_opt_pass pass_cse_sincos; extern struct tree_opt_pass pass_warn_function_return; extern struct tree_opt_pass pass_warn_function_noreturn; extern struct tree_opt_pass pass_phiopt; diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c index 5b2834089ec..ae7a40bf8ca 100644 --- a/gcc/tree-ssa-math-opts.c +++ b/gcc/tree-ssa-math-opts.c @@ -440,14 +440,12 @@ execute_cse_reciprocals_1 (block_stmt_iterator *def_bsi, tree def) occ_head = NULL; } - static bool gate_cse_reciprocals (void) { return optimize && !optimize_size && flag_unsafe_math_optimizations; } - /* Go through all the floating-point SSA_NAMEs, and call execute_cse_reciprocals_1 on each of them. */ static unsigned int @@ -490,6 +488,7 @@ execute_cse_reciprocals (void) for (bsi = bsi_after_labels (bb); !bsi_end_p (bsi); bsi_next (&bsi)) { tree stmt = bsi_stmt (bsi); + if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT && (def = SINGLE_SSA_TREE_OPERAND (stmt, SSA_OP_DEF)) != NULL && FLOAT_TYPE_P (TREE_TYPE (def)) @@ -521,3 +520,208 @@ struct tree_opt_pass pass_cse_reciprocals = | TODO_verify_stmts, /* todo_flags_finish */ 0 /* letter */ }; + +/* Records an occurance at statement USE_STMT in the vector of trees + STMTS if it is dominated by *TOP_BB or dominates it or this basic block + is not yet initialized. Returns true if the occurance was pushed on + the vector. Adjusts *TOP_BB to be the basic block dominating all + statements in the vector. */ + +static bool +maybe_record_sincos (VEC(tree, heap) **stmts, + basic_block *top_bb, tree use_stmt) +{ + basic_block use_bb = bb_for_stmt (use_stmt); + if (*top_bb + && (*top_bb == use_bb + || dominated_by_p (CDI_DOMINATORS, use_bb, *top_bb))) + VEC_safe_push (tree, heap, *stmts, use_stmt); + else if (!*top_bb + || dominated_by_p (CDI_DOMINATORS, *top_bb, use_bb)) + { + VEC_safe_push (tree, heap, *stmts, use_stmt); + *top_bb = use_bb; + } + else + return false; + + return true; +} + +/* Look for sin, cos and cexpi calls with the same argument NAME and + create a single call to cexpi CSEing the result in this case. + We first walk over all immediate uses of the argument collecting + statements that we can CSE in a vector and in a second pass replace + the statement rhs with a REALPART or IMAGPART expression on the + result of the cexpi call we insert before the use statement that + dominates all other candidates. */ + +static void +execute_cse_sincos_1 (tree name) +{ + block_stmt_iterator bsi; + imm_use_iterator use_iter; + tree def_stmt, use_stmt, fndecl, res, call, stmt, type; + int seen_cos = 0, seen_sin = 0, seen_cexpi = 0; + VEC(tree, heap) *stmts = NULL; + basic_block top_bb = NULL; + int i; + + type = TREE_TYPE (name); + FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, name) + { + if (TREE_CODE (use_stmt) != GIMPLE_MODIFY_STMT + || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) != CALL_EXPR + || !(fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (use_stmt, 1))) + || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL) + continue; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + CASE_FLT_FN (BUILT_IN_COS): + seen_cos |= maybe_record_sincos (&stmts, &top_bb, use_stmt) ? 1 : 0; + break; + + CASE_FLT_FN (BUILT_IN_SIN): + seen_sin |= maybe_record_sincos (&stmts, &top_bb, use_stmt) ? 1 : 0; + break; + + CASE_FLT_FN (BUILT_IN_CEXPI): + seen_cexpi |= maybe_record_sincos (&stmts, &top_bb, use_stmt) ? 1 : 0; + break; + + default:; + } + } + + if (seen_cos + seen_sin + seen_cexpi <= 1) + { + VEC_free(tree, heap, stmts); + return; + } + + /* Simply insert cexpi at the beginning of top_bb but not earlier than + the name def statement. */ + fndecl = mathfn_built_in (type, BUILT_IN_CEXPI); + if (!fndecl) + return; + res = make_rename_temp (TREE_TYPE (TREE_TYPE (fndecl)), "sincostmp"); + call = build_function_call_expr (fndecl, build_tree_list (NULL_TREE, name)); + stmt = build2 (GIMPLE_MODIFY_STMT, NULL_TREE, res, call); + def_stmt = SSA_NAME_DEF_STMT (name); + if (bb_for_stmt (def_stmt) == top_bb + && TREE_CODE (def_stmt) == GIMPLE_MODIFY_STMT) + { + bsi = bsi_for_stmt (def_stmt); + bsi_insert_after (&bsi, stmt, BSI_SAME_STMT); + } + else + { + bsi = bsi_after_labels (top_bb); + bsi_insert_before (&bsi, stmt, BSI_SAME_STMT); + } + update_stmt (stmt); + + /* And adjust the recorded old call sites. */ + for (i = 0; VEC_iterate(tree, stmts, i, use_stmt); ++i) + { + fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (use_stmt, 1)); + switch (DECL_FUNCTION_CODE (fndecl)) + { + CASE_FLT_FN (BUILT_IN_COS): + GIMPLE_STMT_OPERAND (use_stmt, 1) = fold_build1 (REALPART_EXPR, + type, res); + break; + + CASE_FLT_FN (BUILT_IN_SIN): + GIMPLE_STMT_OPERAND (use_stmt, 1) = fold_build1 (IMAGPART_EXPR, + type, res); + break; + + CASE_FLT_FN (BUILT_IN_CEXPI): + GIMPLE_STMT_OPERAND (use_stmt, 1) = res; + break; + + default:; + gcc_unreachable (); + } + + update_stmt (use_stmt); + } + + VEC_free(tree, heap, stmts); +} + +/* Go through all calls to sin, cos and cexpi and call execute_cse_sincos_1 + on the SSA_NAME argument of each of them. */ + +static unsigned int +execute_cse_sincos (void) +{ + basic_block bb; + + calculate_dominance_info (CDI_DOMINATORS); + + FOR_EACH_BB (bb) + { + block_stmt_iterator bsi; + + for (bsi = bsi_after_labels (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree stmt = bsi_stmt (bsi); + tree fndecl; + + if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT + && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR + && (fndecl = get_callee_fndecl (GIMPLE_STMT_OPERAND (stmt, 1))) + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + { + tree arg; + + switch (DECL_FUNCTION_CODE (fndecl)) + { + CASE_FLT_FN (BUILT_IN_COS): + CASE_FLT_FN (BUILT_IN_SIN): + CASE_FLT_FN (BUILT_IN_CEXPI): + arg = GIMPLE_STMT_OPERAND (stmt, 1); + arg = TREE_VALUE (TREE_OPERAND (arg, 1)); + if (TREE_CODE (arg) == SSA_NAME) + execute_cse_sincos_1 (arg); + break; + + default:; + } + } + } + } + + free_dominance_info (CDI_DOMINATORS); + return 0; +} + +static bool +gate_cse_sincos (void) +{ + /* Make sure we have either sincos or cexp. */ + return (TARGET_HAS_SINCOS + || TARGET_C99_FUNCTIONS) + && optimize; +} + +struct tree_opt_pass pass_cse_sincos = +{ + "sincos", /* name */ + gate_cse_sincos, /* gate */ + execute_cse_sincos, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_update_ssa | TODO_verify_ssa + | TODO_verify_stmts, /* todo_flags_finish */ + 0 /* letter */ +};