From c2699190282c19bd5703de0a606b842774715074 Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Wed, 4 Jun 2008 21:49:45 +0000 Subject: [PATCH] tree-call-cdce.c: New file. 2008-06-04 Xinliang David Li * tree-call-cdce.c: New file. (cond_dead_built_in_calls): New static variable. (input_domain): New struct. (check_pow): New function. (check_builtin_call): Ditto. (check_target_format): Ditto. (is_call_dce_candidate): Ditto. (gen_one_condition): Ditto. (gen_conditions_for_domain): Ditto. (get_domain): Ditto. (gen_conditions_for_pow_cst_base): Ditto. (gen_conditions_for_pow_int_base): Ditto. (gen_conditions_for_pow): Ditto. (get_no_error_domain): Ditto. (gen_shrink_wrap_conditions): Ditto. (shrink_wrap_one_built_in_call): Ditto. (shink_wrap_conditional_dead_built_in_calls): Ditto. (tree_call_cdce): Ditto. (gate_call_cdce): Ditto. (pass_call_cdce): New gimple pass. * passes.c: (init_optimization_passes): New pass. * tree-pass.h: New pass declaration. * opts.c (decode_options): New flag setting. * common.opt: Add -ftree-builtin-call-dce flag. * Makefile.in: Add new source file. * tempvar.def: New tv_id. * doc/invoke.texi (-ftree-builtin-call-dce): New flag. 2008-06-04 Xinliang David Li * gcc.dg/cdce1.c: New test. * gcc.dg/cdce2.c: Ditto. * g++.dg/cdce3.C: Ditto. From-SVN: r136374 --- gcc/ChangeLog | 30 ++ gcc/Makefile.in | 5 + gcc/common.opt | 4 + gcc/doc/invoke.texi | 13 +- gcc/opts.c | 2 + gcc/passes.c | 8 + gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.dg/cdce3.C | 200 ++++++++ gcc/testsuite/gcc.dg/cdce1.c | 80 +++ gcc/testsuite/gcc.dg/cdce2.c | 55 ++ gcc/timevar.def | 1 + gcc/tree-call-cdce.c | 944 +++++++++++++++++++++++++++++++++++ gcc/tree-pass.h | 1 + 13 files changed, 1346 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cdce3.C create mode 100644 gcc/testsuite/gcc.dg/cdce1.c create mode 100644 gcc/testsuite/gcc.dg/cdce2.c create mode 100644 gcc/tree-call-cdce.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ed7c58c5c82..96de8a05690 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,33 @@ +2008-06-04 Xinliang David Li + + * tree-call-cdce.c: New file. + (cond_dead_built_in_calls): New static variable. + (input_domain): New struct. + (check_pow): New function. + (check_builtin_call): Ditto. + (check_target_format): Ditto. + (is_call_dce_candidate): Ditto. + (gen_one_condition): Ditto. + (gen_conditions_for_domain): Ditto. + (get_domain): Ditto. + (gen_conditions_for_pow_cst_base): Ditto. + (gen_conditions_for_pow_int_base): Ditto. + (gen_conditions_for_pow): Ditto. + (get_no_error_domain): Ditto. + (gen_shrink_wrap_conditions): Ditto. + (shrink_wrap_one_built_in_call): Ditto. + (shink_wrap_conditional_dead_built_in_calls): Ditto. + (tree_call_cdce): Ditto. + (gate_call_cdce): Ditto. + (pass_call_cdce): New gimple pass. + * passes.c: (init_optimization_passes): New pass. + * tree-pass.h: New pass declaration. + * opts.c (decode_options): New flag setting. + * common.opt: Add -ftree-builtin-call-dce flag. + * Makefile.in: Add new source file. + * tempvar.def: New tv_id. + * doc/invoke.texi (-ftree-builtin-call-dce): New flag. + 2008-06-04 Richard Guenther * tree-flow-inline.h (is_global_var): Do not check TREE_STATIC diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 3e032df293f..f66b7405db2 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1143,6 +1143,7 @@ OBJS-common = \ toplev.o \ tracer.o \ tree-affine.o \ + tree-call-cdce.o \ tree-cfg.o \ tree-cfgcleanup.o \ tree-chrec.o \ @@ -2603,6 +2604,10 @@ tree-ssa-dce.o : tree-ssa-dce.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \ coretypes.h $(TREE_DUMP_H) tree-pass.h $(FLAGS_H) $(BASIC_BLOCK_H) \ $(GGC_H) hard-reg-set.h $(OBSTACK_H) $(TREE_GIMPLE_H) $(CFGLOOP_H) \ $(SCEV_H) +tree-call-cdce.o : tree-call-cdce.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \ + $(RTL_H) $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) \ + coretypes.h $(TREE_DUMP_H) tree-pass.h $(FLAGS_H) $(BASIC_BLOCK_H) \ + $(GGC_H) hard-reg-set.h $(OBSTACK_H) $(TREE_GIMPLE_H) tree-ssa-ccp.o : tree-ssa-ccp.c $(TREE_FLOW_H) $(CONFIG_H) \ $(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h \ $(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \ diff --git a/gcc/common.opt b/gcc/common.opt index 7af5c783c68..94f2c56a362 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1255,6 +1255,10 @@ fweb Common Report Var(flag_web) Init(2) Optimization Construct webs and split unrelated uses of single variable +ftree-builtin-call-dce +Common Report Var(flag_tree_builtin_call_dce) Init(0) Optimization +Enable conditional dead code elimination for builtin calls + fwhole-program Common Report Var(flag_whole_program) Init(0) Optimization Perform whole program optimizations diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index ece9d9d5fc0..0b5ad590a78 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -352,8 +352,9 @@ Objective-C and Objective-C++ Dialects}. -fschedule-insns -fschedule-insns2 -fsection-anchors -fsee @gol -fsignaling-nans -fsingle-precision-constant -fsplit-ivs-in-unroller @gol -fsplit-wide-types -fstack-protector -fstack-protector-all @gol --fstrict-aliasing -fstrict-overflow -fthread-jumps -ftracer -ftree-ccp @gol --ftree-ch -ftree-copy-prop -ftree-copyrename -ftree-dce @gol +-fstrict-aliasing -fstrict-overflow -fthread-jumps -ftracer @gol +-ftree-builtin-call-dce -ftree-ccp -ftree-ch -ftree-copy-prop @gol +-ftree-copyrename -ftree-dce @gol -ftree-dominator-opts -ftree-dse -ftree-fre -ftree-loop-im @gol -ftree-loop-distribution @gol -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize @gol @@ -5154,6 +5155,7 @@ compilation time. -fipa-reference @gol -fmerge-constants -fsplit-wide-types @gol +-ftree-builtin-call-dce @gol -ftree-ccp @gol -ftree-ch @gol -ftree-copyrename @gol @@ -5874,6 +5876,13 @@ enabled by default at @option{-O2} and higher. Perform dead code elimination (DCE) on trees. This flag is enabled by default at @option{-O} and higher. +@item -ftree-builtin-call-dce +@opindex ftree-builtin-call-dce +Perform conditional dead code elimination (DCE) for calls to builtin functions +that may set @code{errno} but are otherwise side-effect free. This flag is +enabled by default at @option{-O2} and higher if @option{-Os} is not also +specified. + @item -ftree-dominator-opts @opindex ftree-dominator-opts Perform a variety of simple scalar cleanups (constant/copy diff --git a/gcc/opts.c b/gcc/opts.c index 7add8d3d250..d86c2b34be8 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -889,6 +889,8 @@ decode_options (unsigned int argc, const char **argv) if (!optimize_size) { + /* Conditional DCE generates bigger code. */ + flag_tree_builtin_call_dce = 1; /* PRE tends to generate bigger code. */ flag_tree_pre = 1; } diff --git a/gcc/passes.c b/gcc/passes.c index 56862d88a5d..4f19f7af1e1 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -561,6 +561,14 @@ init_optimization_passes (void) NEXT_PASS (pass_copy_prop); NEXT_PASS (pass_merge_phi); NEXT_PASS (pass_dce); + /* Ideally the function call conditional + dead code elimination phase can be delayed + till later where potentially more opportunities + can be found. Due to lack of good ways to + update VDEFs associated with the shrink-wrapped + calls, it is better to do the transformation + here where memory SSA is not built yet. */ + NEXT_PASS (pass_call_cdce); NEXT_PASS (pass_update_address_taken); NEXT_PASS (pass_simple_dse); NEXT_PASS (pass_tail_recursion); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 14414d95901..5ada62bc7c4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2008-06-04 Xinliang David Li + * gcc.dg/cdce1.c: New test. + * gcc.dg/cdce2.c: Ditto. + * g++.dg/cdce3.C: Ditto. + 2008-06-04 Janus Weil PR fortran/36322 diff --git a/gcc/testsuite/g++.dg/cdce3.C b/gcc/testsuite/g++.dg/cdce3.C new file mode 100644 index 00000000000..e1e8509f2ac --- /dev/null +++ b/gcc/testsuite/g++.dg/cdce3.C @@ -0,0 +1,200 @@ +/* { dg-do run { target { ! "*-*-darwin" } } } */ +/* { dg-options "-O2 -fmath-errno -fdump-tree-cdce-details -lm" } */ +/* { dg-final { scan-tree-dump "cdce3.C:68: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:69: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:70: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:71: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:72: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:73: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:74: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:75: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:76: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:77: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:78: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:79: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:80: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:81: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:82: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { scan-tree-dump "cdce3.C:83: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { cleanup-tree-dump "cdce" } } */ +#include +#include +#ifdef DEBUG +#include +#endif +#include +typedef long double ldouble; +typedef void (*FP) (int xp); +#define NI __attribute__((noinline)) +ldouble result; + +#define DEF_MATH_FUNC(prefix, name) NI void prefix##name##f (int x) \ +{ \ + float yy = name##f ((float) x); \ + STORE_RESULT; \ +} \ +NI void prefix##name (int x) \ +{ \ + double yy = name ((double)x); \ + STORE_RESULT; \ +} \ +NI void prefix##name##l (int x) \ +{ \ + ldouble yy = name##l ((ldouble)x); \ + STORE_RESULT; \ +} + +#undef STORE_RESULT +#define STORE_RESULT result = yy +DEF_MATH_FUNC (m,pow10) +DEF_MATH_FUNC (m,exp10) +DEF_MATH_FUNC (m,exp2) +DEF_MATH_FUNC (m,exp) +DEF_MATH_FUNC (m,expm1) +DEF_MATH_FUNC (m,cosh) +DEF_MATH_FUNC (m,sinh) +DEF_MATH_FUNC (m,acos) +DEF_MATH_FUNC (m,asin) +DEF_MATH_FUNC (m,acosh) +DEF_MATH_FUNC (m,atanh) +DEF_MATH_FUNC (m,log) +DEF_MATH_FUNC (m,log2) +DEF_MATH_FUNC (m,log10) +DEF_MATH_FUNC (m,log1p) +DEF_MATH_FUNC (m,sqrt) + +#undef STORE_RESULT +#define STORE_RESULT +DEF_MATH_FUNC (o,pow10) +DEF_MATH_FUNC (o,exp10) +DEF_MATH_FUNC (o,exp2) +DEF_MATH_FUNC (o,exp) +DEF_MATH_FUNC (o,expm1) +DEF_MATH_FUNC (o,cosh) +DEF_MATH_FUNC (o,sinh) +DEF_MATH_FUNC (o,acos) +DEF_MATH_FUNC (o,asin) +DEF_MATH_FUNC (o,acosh) +DEF_MATH_FUNC (o,atanh) +DEF_MATH_FUNC (o,log) +DEF_MATH_FUNC (o,log2) +DEF_MATH_FUNC (o,log10) +DEF_MATH_FUNC (o,log1p) +DEF_MATH_FUNC (o,sqrt) + +#define INIT_MATH_FUNC(prefix, name, lb, ub) { prefix##name##f, #name "f", 0, 0, lb, ub }, \ +{ prefix##name, #name, 0, 0, lb, ub }, \ +{ prefix##name##l, #name "l" , 0, 0, lb, ub }, + +struct MathFuncInfo +{ + FP math_func; + const char* name; + int lb; + int ub; + bool has_lb; + bool has_ub; +} math_func_arr[] = { + INIT_MATH_FUNC (m,pow10, false, true) + INIT_MATH_FUNC (m,exp10, false, true) + INIT_MATH_FUNC (m,exp2, false, true) + INIT_MATH_FUNC (m,expm1, false, true) + INIT_MATH_FUNC (m,exp, false, true) + INIT_MATH_FUNC (m,cosh, true, true) + INIT_MATH_FUNC (m,sinh, true, true) + INIT_MATH_FUNC (m,acos, true, true) + INIT_MATH_FUNC (m,asin, true, true) + INIT_MATH_FUNC (m,acosh, true, false) + INIT_MATH_FUNC (m,atanh, true, true) + INIT_MATH_FUNC (m,log10, true, false) + INIT_MATH_FUNC (m,log, true, false) + INIT_MATH_FUNC (m,log2, true, false) + INIT_MATH_FUNC (m,log1p, true, false) + INIT_MATH_FUNC (m,sqrt, true, false) + { 0, 0, 0, 0, 0, 0} }; + +MathFuncInfo opt_math_func_arr[] = +{ INIT_MATH_FUNC (o,pow10, false, true) + INIT_MATH_FUNC (o,exp10, false, true) + INIT_MATH_FUNC (o,exp2, false, true) + INIT_MATH_FUNC (o,expm1, false, true) + INIT_MATH_FUNC (o,exp, false, true) + INIT_MATH_FUNC (o,cosh, true, true) + INIT_MATH_FUNC (o,sinh, true, true) + INIT_MATH_FUNC (o,acos, true, true) + INIT_MATH_FUNC (o,asin, true, true) + INIT_MATH_FUNC (o,acosh, true, false) + INIT_MATH_FUNC (o,atanh, true, true) + INIT_MATH_FUNC (o,log10, true, false) + INIT_MATH_FUNC (o,log, true, false) + INIT_MATH_FUNC (o,log2, true, false) + INIT_MATH_FUNC (o,log1p, true, false) + INIT_MATH_FUNC (o,sqrt, true, false) + { 0, 0, 0, 0, 0, 0} }; + +int test (MathFuncInfo* math_func_infos) +{ + int i = 0; + int te = 0; + + for (i = 0; math_func_infos[i].math_func; i++) + { + MathFuncInfo& info = math_func_infos[i]; + int j; + if (info.has_lb) + { + for (j = 0; j > -500000; j--) + { + + errno = 0; + info.math_func (j); + if (errno != 0) + { + te++; + info.lb = j ; + break; + } + } + } + if (info.has_ub) + { + for (j = 0; j < 500000; j++) + { + errno = 0; + info.math_func (j); + if (errno != 0) + { + te++; + info.ub = j ; + break; + } + } + } + } + return te; +} + +int main() +{ + int te1, te2; + + te1 = test (&math_func_arr[0]); + te2 = test (&opt_math_func_arr[0]); + + // Now examine the result + int i = 0; + int errcnt = 0; + for (i = 0; math_func_arr[i].math_func; i++) + { + MathFuncInfo& info = math_func_arr[i]; + MathFuncInfo& opt_info = opt_math_func_arr[i]; +#ifdef DEBUG + fprintf (stderr," %s: lb = %d, ub = %d: lb_opt = %d, ub_opt = %d\n", + info.name, info.lb, info.ub, opt_info.lb, opt_info.ub); +#endif + if (info.lb != opt_info.lb) errcnt ++; + if (info.ub != opt_info.ub) errcnt ++; + } + if (errcnt) abort(); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/cdce1.c b/gcc/testsuite/gcc.dg/cdce1.c new file mode 100644 index 00000000000..26d38ae40f5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cdce1.c @@ -0,0 +1,80 @@ +/* { dg-do run { target { ! "*-*-darwin" } } } */ +/* { dg-options "-O2 -fmath-errno -fdump-tree-cdce-details -lm" } */ +/* { dg-final { scan-tree-dump "cdce1.c:16: note: function call is shrink-wrapped into error conditions\." "cdce" } } */ +/* { dg-final { cleanup-tree-dump "cdce" } } */ + + +#include +#include +#include +int total_err_count = 0; +double foo_opt (int x, double y) __attribute__((noinline)); +double foo_opt (int x, double y) +{ + double yy = 0; + errno = 0; + yy = pow (x, y * y); + return 0; +} + +double foo (int x, double y) __attribute__((noinline)); +double foo (int x, double y) +{ + double yy = 0; + errno = 0; + yy = pow (x, y * y); + return yy; +} + +int test (double (*fp)(int x, double y)) +{ + int i,x; + + x = 127; + for (i = 30; i < 300; i++) + { + fp (x, i); + if (errno) + total_err_count ++; + } + + x = -300; + for (i = 100; i < 300; i++) + { + fp (x, i); + if (errno) + total_err_count ++; + } + + x = 65577; + for (i = 60; i < 200; i++) + { + fp (x, i); + if (errno) + total_err_count ++; + } + + x = 65577 * 127; + for (i = 1; i < 100; i++) + { + fp (x, i); + if (errno) + total_err_count ++; + } + + return total_err_count; +} + +int main () +{ + int en1, en2; + total_err_count = 0; + en1 = test (foo_opt); + total_err_count = 0; + en2 = test (foo); + + if (en1 != en2) + abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/cdce2.c b/gcc/testsuite/gcc.dg/cdce2.c new file mode 100644 index 00000000000..ba9e4962050 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cdce2.c @@ -0,0 +1,55 @@ +/* { dg-do run { target { ! "*-*-darwin" } } } */ +/* { dg-options "-O2 -fmath-errno -fdump-tree-cdce-details -lm" } */ +/* { dg-final { scan-tree-dump "cdce2.c:16: note: function call is shrink-wrapped into error conditions\." "cdce" } }*/ +/* { dg-final { cleanup-tree-dump "cdce" } } */ + + +#include +#include +#include +int total_err_count = 0; +double foo_opt (double y) __attribute__((noinline)); +double foo_opt (double y) +{ + double yy = 0; + errno = 0; + yy = log (y); + return 0; +} + +double foo (double y) __attribute__((noinline)); +double foo (double y) +{ + double yy = 0; + errno = 0; + yy = log (y); + return yy; +} + +int test (double (*fp) (double y)) +{ + int i,x; + for (i = -100; i < 100; i++) + { + fp (i); + if (errno) + total_err_count ++; + } + + return total_err_count; +} + +int main () +{ + int en1, en2; + double yy; + total_err_count = 0; + en1 = test (foo_opt); + total_err_count = 0; + en2 = test (foo); + + if (en1 != en2) + abort(); + + return 0; +} diff --git a/gcc/timevar.def b/gcc/timevar.def index 954bc6dc671..0429eb41275 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -113,6 +113,7 @@ DEFTIMEVAR (TV_TREE_FORWPROP , "tree forward propagate") DEFTIMEVAR (TV_TREE_PHIPROP , "tree phiprop") DEFTIMEVAR (TV_TREE_DCE , "tree conservative DCE") DEFTIMEVAR (TV_TREE_CD_DCE , "tree aggressive DCE") +DEFTIMEVAR (TV_TREE_CALL_CDCE , "tree buildin call DCE") DEFTIMEVAR (TV_TREE_DSE , "tree DSE") DEFTIMEVAR (TV_TREE_MERGE_PHI , "PHI merge") DEFTIMEVAR (TV_TREE_LOOP , "tree loop optimization") diff --git a/gcc/tree-call-cdce.c b/gcc/tree-call-cdce.c new file mode 100644 index 00000000000..4be0cf9bd89 --- /dev/null +++ b/gcc/tree-call-cdce.c @@ -0,0 +1,944 @@ +/* Conditional Dead Call Elimination pass for the GNU compiler. + Copyright (C) 2008 + Free Software Foundation, Inc. + Contributed by Xinliang David Li + +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 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +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 COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "ggc.h" + +/* These RTL headers are needed for basic-block.h. */ +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "obstack.h" +#include "basic-block.h" + +#include "tree.h" +#include "diagnostic.h" +#include "tree-flow.h" +#include "tree-gimple.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "timevar.h" +#include "flags.h" + + +/* Conditional dead call elimination + + Some builtin functions can set errno on error conditions, but they + are otherwise pure. If the result of a call to such a function is + not used, the compiler can still not eliminate the call without + powerful interprocedural analysis to prove that the errno is not + checked. However, if the conditions under which the error occurs + are known, the compiler can conditionally dead code eliminate the + calls by shrink-wrapping the semi-dead calls into the error condition: + + built_in_call (args) + ==> + if (error_cond (args)) + built_in_call (args) + + An actual simple example is : + log (x); // Mostly dead call + ==> + if (x < 0) + log (x); + With this change, call to log (x) is effectively eliminated, as + in majority of the cases, log won't be called with x out of + range. The branch is totally predictable, so the branch cost + is low. + + Note that library functions are not supposed to clear errno to zero without + error. See IEEE Std 1003.1, section 2.3 Error Numbers, and section 7.5:3 of + ISO/IEC 9899 (C99). + + The condition wrapping the builtin call is conservatively set to avoid too + aggressive (wrong) shrink wrapping. The optimization is called conditional + dead call elimination because the call is eliminated under the condition + that the input arguments would not lead to domain or range error (for + instance when x <= 0 for a log (x) call), however the chances that the error + condition is hit is very low (those builtin calls which are conditionally + dead are usually part of the C++ abstraction penalty exposed after + inlining). */ + + +/* A structure for representing input domain of + a function argument in integer. If the lower + bound is -inf, has_lb is set to false. If the + upper bound is +inf, has_ub is false. + is_lb_inclusive and is_ub_inclusive are flags + to indicate if lb and ub value are inclusive + respectively. */ + +typedef struct input_domain +{ + int lb; + int ub; + bool has_lb; + bool has_ub; + bool is_lb_inclusive; + bool is_ub_inclusive; +} inp_domain; + +static VEC (tree, heap) *cond_dead_built_in_calls; + +/* A helper function to construct and return an input + domain object. LB is the lower bound, HAS_LB is + a boolean flag indicating if the lower bound exists, + and LB_INCLUSIVE is a boolean flag indicating if the + lower bound is inclusive or not. UB, HAS_UB, and + UB_INCLUSIVE have the same meaning, but for upper + bound of the domain. */ + +static inp_domain +get_domain (int lb, bool has_lb, bool lb_inclusive, + int ub, bool has_ub, bool ub_inclusive) +{ + inp_domain domain; + domain.lb = lb; + domain.has_lb = has_lb; + domain.is_lb_inclusive = lb_inclusive; + domain.ub = ub; + domain.has_ub = has_ub; + domain.is_ub_inclusive = ub_inclusive; + return domain; +} + +/* A helper function to check the target format for the + argument type. In this implementation, only IEEE formats + are supported. ARG is the call argument to be checked. + Returns true if the format is supported. To support other + target formats, function get_no_error_domain needs to be + enhanced to have range bounds properly computed. Since + the check is cheap (very small number of candidates + to be checked), the result is not cached for each float type. */ + +static bool +check_target_format (tree arg) +{ + tree type; + enum machine_mode mode; + const struct real_format *rfmt; + + type = TREE_TYPE (arg); + mode = TYPE_MODE (type); + rfmt = REAL_MODE_FORMAT (mode); + if ((mode == SFmode && rfmt == &ieee_single_format) + || (mode == DFmode && rfmt == &ieee_double_format) + /* For long double, we can not really check XFmode + which is only defined on intel platforms. + Candidate pre-selection using builtin function + code guarantees that we are checking formats + for long double modes: double, quad, and extended. */ + || (mode != SFmode && mode != DFmode + && (rfmt == &ieee_quad_format + || rfmt == &ieee_extended_intel_96_format + || rfmt == &ieee_extended_intel_128_format + || rfmt == &ieee_extended_intel_96_round_53_format))) + return true; + + return false; +} + + +/* A helper function to help select calls to pow that are suitable for + conditional DCE transformation. It looks for pow calls that can be + guided with simple conditions. Such calls either have constant base + values or base values converted from integers. Returns true if + the pow call POW_CALL is a candidate. */ + +/* The maximum integer bit size for base argument of a pow call + that is suitable for shrink-wrapping transformation. */ +#define MAX_BASE_INT_BIT_SIZE 32 + +static bool +check_pow (tree pow_call) +{ + tree base, expn; + enum tree_code bc, ec; + + if (call_expr_nargs (pow_call) != 2) + return false; + + base = CALL_EXPR_ARG (pow_call, 0); + expn = CALL_EXPR_ARG (pow_call, 1); + + if (!check_target_format (expn)) + return false; + + bc = TREE_CODE (base); + ec = TREE_CODE (expn); + + /* Folding candidates are not interesting. + Can actually assert that it is already folded. */ + if (ec == REAL_CST && bc == REAL_CST) + return false; + + if (bc == REAL_CST) + { + /* Only handle a fixed range of constant. */ + REAL_VALUE_TYPE mv; + REAL_VALUE_TYPE bcv = TREE_REAL_CST (base); + if (REAL_VALUES_EQUAL (bcv, dconst1)) + return false; + if (REAL_VALUES_LESS (bcv, dconst1)) + return false; + real_from_integer (&mv, TYPE_MODE (TREE_TYPE (base)), 256, 0, 1); + if (REAL_VALUES_LESS (mv, bcv)) + return false; + return true; + } + else if (bc == SSA_NAME) + { + tree base_def, base_val, base_val0, base_var, type; + int bit_sz; + + /* Only handles cases where base value is converted + from integer values. */ + base_def = SSA_NAME_DEF_STMT (base); + if (TREE_CODE (base_def) != GIMPLE_MODIFY_STMT) + return false; + + base_val = GIMPLE_STMT_OPERAND (base_def, 1); + + if (TREE_CODE (base_val) != FLOAT_EXPR) + return false; + base_val0 = TREE_OPERAND (base_val, 0); + + base_var = SSA_NAME_VAR (base_val0); + if (!DECL_P (base_var)) + return false; + + type = TREE_TYPE (base_var); + if (TREE_CODE (type) != INTEGER_TYPE) + return false; + bit_sz = TYPE_PRECISION (type); + /* If the type of the base is too wide, + the resulting shrink wrapping condition + will be too conservative. */ + if (bit_sz > MAX_BASE_INT_BIT_SIZE) + return false; + + return true; + } + else + return false; +} + +/* A helper function to help select candidate function calls that are + suitable for conditional DCE. Candidate functions must have single + valid input domain in this implementation except for pow (see check_pow). + Returns true if the function call is a candidate. */ + +static bool +check_builtin_call (tree bcall) +{ + tree arg; + + arg = CALL_EXPR_ARG (bcall, 0); + return check_target_format (arg); +} + +/* A helper function to determine if a builtin function call is a + candidate for conditional DCE. Returns true if the builtin call + is a candidate. */ + +static bool +is_call_dce_candidate (tree call) +{ + tree fn; + enum built_in_function fnc; + + if (!flag_tree_builtin_call_dce) + return false; + + gcc_assert (call && TREE_CODE (call) == CALL_EXPR); + + fn = get_callee_fndecl (call); + if (!fn || !DECL_BUILT_IN (fn) + || (DECL_BUILT_IN_CLASS (fn) != BUILT_IN_NORMAL)) + return false; + + fnc = DECL_FUNCTION_CODE (fn); + switch (fnc) + { + /* Trig functions. */ + CASE_FLT_FN (BUILT_IN_ACOS): + CASE_FLT_FN (BUILT_IN_ASIN): + /* Hyperbolic functions. */ + CASE_FLT_FN (BUILT_IN_ACOSH): + CASE_FLT_FN (BUILT_IN_ATANH): + CASE_FLT_FN (BUILT_IN_COSH): + CASE_FLT_FN (BUILT_IN_SINH): + /* Log functions. */ + CASE_FLT_FN (BUILT_IN_LOG): + CASE_FLT_FN (BUILT_IN_LOG2): + CASE_FLT_FN (BUILT_IN_LOG10): + CASE_FLT_FN (BUILT_IN_LOG1P): + /* Exp functions. */ + CASE_FLT_FN (BUILT_IN_EXP): + CASE_FLT_FN (BUILT_IN_EXP2): + CASE_FLT_FN (BUILT_IN_EXP10): + CASE_FLT_FN (BUILT_IN_EXPM1): + CASE_FLT_FN (BUILT_IN_POW10): + /* Sqrt. */ + CASE_FLT_FN (BUILT_IN_SQRT): + return check_builtin_call (call); + /* Special one: two argument pow. */ + case BUILT_IN_POW: + return check_pow (call); + default: + break; + } + + return false; +} + + +/* A helper function to generate gimple statements for + one bound comparison. ARG is the call argument to + be compared with the bound, LBUB is the bound value + in integer, TCODE is the tree_code of the comparison, + TEMP_NAME1/TEMP_NAME2 are names of the temporaries, + CONDS is a vector holding the produced GIMPLE statements, + and NCONDS points to the variable holding the number + of logical comparisons. CONDS is either empty or + a list ended with a null tree. */ + +static void +gen_one_condition (tree arg, int lbub, + enum tree_code tcode, + const char *temp_name1, + const char *temp_name2, + VEC (tree, heap) *conds, + unsigned *nconds) +{ + tree lbub_real_cst, lbub_cst, float_type; + tree temp, tempn, tempc, tempcn; + tree stmt1, stmt2, stmt3; + + float_type = TREE_TYPE (arg); + lbub_cst = build_int_cst (integer_type_node, lbub); + lbub_real_cst = build_real_from_int_cst (float_type, lbub_cst); + + temp = create_tmp_var (float_type, temp_name1); + stmt1 = build_gimple_modify_stmt (temp, arg); + tempn = make_ssa_name (temp, stmt1); + GIMPLE_STMT_OPERAND (stmt1, 0) = tempn; + + tempc = create_tmp_var (boolean_type_node, temp_name2); + stmt2 = build_gimple_modify_stmt (tempc, + fold_build2 (tcode, + boolean_type_node, + tempn, lbub_real_cst)); + tempcn = make_ssa_name (tempc, stmt2); + GIMPLE_STMT_OPERAND (stmt2, 0) = tempcn; + + /* fold_built3 not used for gimple statement here, + as it will hit assertion. */ + stmt3 = build3 (COND_EXPR, void_type_node, + tempcn, NULL_TREE, NULL_TREE); + VEC_quick_push (tree, conds, stmt1); + VEC_quick_push (tree, conds, stmt2); + VEC_quick_push (tree, conds, stmt3); + (*nconds)++; +} + +/* A helper function to generate GIMPLE statements for + out of input domain check. ARG is the call argument + to be runtime checked, DOMAIN holds the valid domain + for the given function, CONDS points to the vector + holding the result GIMPLE statements. *NCONDS is + the number of logical comparisons. This function + produces no more than two logical comparisons, one + for lower bound check, one for upper bound check. */ + +static void +gen_conditions_for_domain (tree arg, inp_domain domain, + VEC (tree, heap) *conds, + unsigned *nconds) +{ + if (domain.has_lb) + gen_one_condition (arg, domain.lb, + (domain.is_lb_inclusive + ? LT_EXPR : LE_EXPR), + "DCE_COND_LB", "DCE_COND_LB_TEST", + conds, nconds); + + if (domain.has_ub) + { + /* Now push a separator. */ + if (domain.has_lb) + VEC_quick_push (tree, conds, NULL); + + gen_one_condition (arg, domain.ub, + (domain.is_ub_inclusive + ? GT_EXPR : GE_EXPR), + "DCE_COND_UB", "DCE_COND_UB_TEST", + conds, nconds); + } +} + + +/* A helper function to generate condition + code for the y argument in call pow (some_const, y). + See candidate selection in check_pow. Since the + candidates' base values have a limited range, + the guarded code generated for y are simple: + if (y > max_y) + pow (const, y); + Note max_y can be computed separately for each + const base, but in this implementation, we + choose to compute it using the max base + in the allowed range for the purpose of + simplicity. BASE is the constant base value, + EXPN is the expression for the exponent argument, + *CONDS is the vector to hold resulting statements, + and *NCONDS is the number of logical conditions. */ + +static void +gen_conditions_for_pow_cst_base (tree base, tree expn, + VEC (tree, heap) *conds, + unsigned *nconds) +{ + inp_domain exp_domain; + /* Validate the range of the base constant to make + sure it is consistent with check_pow. */ + REAL_VALUE_TYPE mv; + REAL_VALUE_TYPE bcv = TREE_REAL_CST (base); + gcc_assert (!REAL_VALUES_EQUAL (bcv, dconst1) + && !REAL_VALUES_LESS (bcv, dconst1)); + real_from_integer (&mv, TYPE_MODE (TREE_TYPE (base)), 256, 0, 1); + gcc_assert (!REAL_VALUES_LESS (mv, bcv)); + + exp_domain = get_domain (0, false, false, + 127, true, false); + + gen_conditions_for_domain (expn, exp_domain, + conds, nconds); +} + +/* Generate error condition code for pow calls with + non constant base values. The candidates selected + have their base argument value converted from + integer (see check_pow) value (1, 2, 4 bytes), and + the max exp value is computed based on the size + of the integer type (i.e. max possible base value). + The resulting input domain for exp argument is thus + conservative (smaller than the max value allowed by + the runtime value of the base). BASE is the integer + base value, EXPN is the expression for the exponent + argument, *CONDS is the vector to hold resulting + statements, and *NCONDS is the number of logical + conditions. */ + +static void +gen_conditions_for_pow_int_base (tree base, tree expn, + VEC (tree, heap) *conds, + unsigned *nconds) +{ + tree base_def, base_nm, base_val, base_val0; + tree base_var, int_type; + tree temp, tempn; + tree cst0, stmt1, stmt2; + int bit_sz, max_exp; + inp_domain exp_domain; + + base_def = SSA_NAME_DEF_STMT (base); + base_nm = GIMPLE_STMT_OPERAND (base_def, 0); + base_val = GIMPLE_STMT_OPERAND (base_def, 1); + base_val0 = TREE_OPERAND (base_val, 0); + base_var = SSA_NAME_VAR (base_val0); + int_type = TREE_TYPE (base_var); + bit_sz = TYPE_PRECISION (int_type); + gcc_assert (bit_sz > 0 + && bit_sz <= MAX_BASE_INT_BIT_SIZE); + + /* Determine the max exp argument value according to + the size of the base integer. The max exp value + is conservatively estimated assuming IEEE754 double + precision format. */ + if (bit_sz == 8) + max_exp = 128; + else if (bit_sz == 16) + max_exp = 64; + else + { + gcc_assert (bit_sz == MAX_BASE_INT_BIT_SIZE); + max_exp = 32; + } + + /* For pow ((double)x, y), generate the following conditions: + cond 1: + temp1 = x; + if (temp1 <= 0) + + cond 2: + temp2 = y; + if (temp2 > max_exp_real_cst) */ + + /* Generate condition in reverse order -- first + the condition for the exp argument. */ + + exp_domain = get_domain (0, false, false, + max_exp, true, true); + + gen_conditions_for_domain (expn, exp_domain, + conds, nconds); + + /* Now generate condition for the base argument. + Note it does not use the helper function + gen_conditions_for_domain because the base + type is integer. */ + + /* Push a separator. */ + VEC_quick_push (tree, conds, NULL); + + temp = create_tmp_var (int_type, "DCE_COND1"); + cst0 = build_int_cst (int_type, 0); + stmt1 = build_gimple_modify_stmt (temp, base_val0); + tempn = make_ssa_name (temp, stmt1); + GIMPLE_STMT_OPERAND (stmt1, 0) = tempn; + stmt2 = build3 (COND_EXPR, void_type_node, + fold_build2 (LE_EXPR, boolean_type_node, tempn, cst0), + NULL_TREE, NULL_TREE); + + VEC_quick_push (tree, conds, stmt1); + VEC_quick_push (tree, conds, stmt2); + (*nconds)++; +} + +/* Method to generate conditional statements for guarding conditionally + dead calls to pow. One or more statements can be generated for + each logical condition. Statement groups of different conditions + are separated by a NULL tree and they are stored in the VEC + conds. The number of logical conditions are stored in *nconds. + + See C99 standard, 7.12.7.4:2, for description of pow (x, y). + The precise condition for domain errors are complex. In this + implementation, a simplified (but conservative) valid domain + for x and y are used: x is positive to avoid dom errors, while + y is smaller than a upper bound (depending on x) to avoid range + errors. Runtime code is generated to check x (if not constant) + and y against the valid domain. If it is out, jump to the call, + otherwise the call is bypassed. POW_CALL is the call statement, + *CONDS is a vector holding the resulting condition statements, + and *NCONDS is the number of logical conditions. */ + +static void +gen_conditions_for_pow (tree pow_call, VEC (tree, heap) *conds, + unsigned *nconds) +{ + tree base, expn; + enum tree_code bc, ec; + +#ifdef ENABLE_CHECKING + gcc_assert (check_pow (pow_call)); +#endif + + *nconds = 0; + + base = CALL_EXPR_ARG (pow_call, 0); + expn = CALL_EXPR_ARG (pow_call, 1); + + bc = TREE_CODE (base); + ec = TREE_CODE (expn); + + if (bc == REAL_CST) + gen_conditions_for_pow_cst_base (base, expn, + conds, nconds); + else if (bc == SSA_NAME) + gen_conditions_for_pow_int_base (base, expn, + conds, nconds); + else + gcc_unreachable (); +} + +/* A helper routine to help computing the valid input domain + for a builtin function. See C99 7.12.7 for details. In this + implementation, we only handle single region domain. The + resulting region can be conservative (smaller) than the actual + one and rounded to integers. Some of the bounds are documented + in the standard, while other limit constants are computed + assuming IEEE floating point format (for SF and DF modes). + Since IEEE only sets minimum requirements for long double format, + different long double formats exist under different implementations + (e.g, 64 bit double precision (DF), 80 bit double-extended + precision (XF), and 128 bit quad precision (QF) ). For simplicity, + in this implementation, the computed bounds for long double assume + 64 bit format (DF), and are therefore conservative. Another + assumption is that single precision float type is always SF mode, + and double type is DF mode. This function is quite + implementation specific, so it may not be suitable to be part of + builtins.c. This needs to be revisited later to see if it can + be leveraged in x87 assembly expansion. */ + +static inp_domain +get_no_error_domain (enum built_in_function fnc) +{ + switch (fnc) + { + /* Trig functions: return [-1, +1] */ + CASE_FLT_FN (BUILT_IN_ACOS): + CASE_FLT_FN (BUILT_IN_ASIN): + return get_domain (-1, true, true, + 1, true, true); + /* Hyperbolic functions. */ + CASE_FLT_FN (BUILT_IN_ACOSH): + /* acosh: [1, +inf) */ + return get_domain (1, true, true, + 1, false, false); + CASE_FLT_FN (BUILT_IN_ATANH): + /* atanh: (-1, +1) */ + return get_domain (-1, true, false, + 1, true, false); + case BUILT_IN_COSHF: + case BUILT_IN_SINHF: + /* coshf: (-89, +89) */ + return get_domain (-89, true, false, + 89, true, false); + case BUILT_IN_COSH: + case BUILT_IN_SINH: + case BUILT_IN_COSHL: + case BUILT_IN_SINHL: + /* cosh: (-710, +710) */ + return get_domain (-710, true, false, + 710, true, false); + /* Log functions: (0, +inf) */ + CASE_FLT_FN (BUILT_IN_LOG): + CASE_FLT_FN (BUILT_IN_LOG2): + CASE_FLT_FN (BUILT_IN_LOG10): + return get_domain (0, true, false, + 0, false, false); + CASE_FLT_FN (BUILT_IN_LOG1P): + return get_domain (-1, true, false, + 0, false, false); + /* Exp functions. */ + case BUILT_IN_EXPF: + case BUILT_IN_EXPM1F: + /* expf: (-inf, 88) */ + return get_domain (-1, false, false, + 88, true, false); + case BUILT_IN_EXP: + case BUILT_IN_EXPM1: + case BUILT_IN_EXPL: + case BUILT_IN_EXPM1L: + /* exp: (-inf, 709) */ + return get_domain (-1, false, false, + 709, true, false); + case BUILT_IN_EXP2F: + /* exp2f: (-inf, 128) */ + return get_domain (-1, false, false, + 128, true, false); + case BUILT_IN_EXP2: + case BUILT_IN_EXP2L: + /* exp2: (-inf, 1024) */ + return get_domain (-1, false, false, + 1024, true, false); + case BUILT_IN_EXP10F: + case BUILT_IN_POW10F: + /* exp10f: (-inf, 38) */ + return get_domain (-1, false, false, + 38, true, false); + case BUILT_IN_EXP10: + case BUILT_IN_POW10: + case BUILT_IN_EXP10L: + case BUILT_IN_POW10L: + /* exp10: (-inf, 308) */ + return get_domain (-1, false, false, + 308, true, false); + /* sqrt: [0, +inf) */ + CASE_FLT_FN (BUILT_IN_SQRT): + return get_domain (0, true, true, + 0, false, false); + default: + gcc_unreachable (); + } + + gcc_unreachable (); +} + +/* The function to generate shrink wrap conditions for a partially + dead builtin call whose return value is not used anywhere, + but has to be kept live due to potential error condition. + BI_CALL is the builtin call, CONDS is the vector of statements + for condition code, NCODES is the pointer to the number of + logical conditions. Statements belonging to different logical + condition are separated by NULL tree in the vector. */ + +static void +gen_shrink_wrap_conditions (tree bi_call, VEC (tree, heap) *conds, + unsigned int *nconds) +{ + tree call, fn; + enum built_in_function fnc; + + gcc_assert (nconds && conds); + gcc_assert (VEC_length (tree, conds) == 0); + gcc_assert (TREE_CODE (bi_call) == GIMPLE_MODIFY_STMT + || TREE_CODE (bi_call) == CALL_EXPR); + + call = bi_call; + if (TREE_CODE (call) == GIMPLE_MODIFY_STMT) + call = get_call_expr_in (bi_call); + + fn = get_callee_fndecl (call); + gcc_assert (fn && DECL_BUILT_IN (fn)); + fnc = DECL_FUNCTION_CODE (fn); + *nconds = 0; + + if (fnc == BUILT_IN_POW) + gen_conditions_for_pow (call, conds, nconds); + else + { + tree arg; + inp_domain domain = get_no_error_domain (fnc); + *nconds = 0; + arg = CALL_EXPR_ARG (bi_call, 0); + gen_conditions_for_domain (arg, domain, conds, nconds); + } + + return; +} + + +/* Probability of the branch (to the call) is taken. */ +#define ERR_PROB 0.01 + +/* The function to shrink wrap a partially dead builtin call + whose return value is not used anywhere, but has to be kept + live due to potential error condition. Returns true if the + transformation actually happens. */ + +static bool +shrink_wrap_one_built_in_call (tree bi_call) +{ + block_stmt_iterator bi_call_bsi; + basic_block bi_call_bb, join_tgt_bb, guard_bb, guard_bb0; + edge join_tgt_in_edge_from_call, join_tgt_in_edge_fall_thru; + edge bi_call_in_edge0, guard_bb_in_edge; + VEC (tree, heap) *conds; + unsigned tn_cond_stmts, nconds; + unsigned ci; + tree cond_expr = NULL; + tree cond_expr_start; + tree bi_call_label_decl; + tree bi_call_label; + + conds = VEC_alloc (tree, heap, 12); + gen_shrink_wrap_conditions (bi_call, conds, &nconds); + + /* This can happen if the condition generator decides + it is not beneficial to do the transformation. Just + return false and do not do any transformation for + the call. */ + if (nconds == 0) + return false; + + bi_call_bb = bb_for_stmt (bi_call); + + /* Now find the join target bb -- split + bi_call_bb if needed. */ + bi_call_bsi = bsi_for_stmt (bi_call); + + join_tgt_in_edge_from_call = split_block (bi_call_bb, bi_call); + bi_call_bsi = bsi_for_stmt (bi_call); + + join_tgt_bb = join_tgt_in_edge_from_call->dest; + + /* Now it is time to insert the first conditional expression + into bi_call_bb and split this bb so that bi_call is + shrink-wrapped. */ + tn_cond_stmts = VEC_length (tree, conds); + cond_expr = NULL; + cond_expr_start = VEC_index (tree, conds, 0); + for (ci = 0; ci < tn_cond_stmts; ci++) + { + tree c = VEC_index (tree, conds, ci); + gcc_assert (c || ci != 0); + if (!c) + break; + bsi_insert_before (&bi_call_bsi, c, BSI_SAME_STMT); + cond_expr = c; + } + nconds--; + ci++; + gcc_assert (cond_expr && TREE_CODE (cond_expr) == COND_EXPR); + + /* Now the label. */ + bi_call_label_decl = create_artificial_label (); + bi_call_label = build1 (LABEL_EXPR, void_type_node, bi_call_label_decl); + bsi_insert_before (&bi_call_bsi, bi_call_label, BSI_SAME_STMT); + + bi_call_in_edge0 = split_block (bi_call_bb, cond_expr); + bi_call_in_edge0->flags &= ~EDGE_FALLTHRU; + bi_call_in_edge0->flags |= EDGE_TRUE_VALUE; + guard_bb0 = bi_call_bb; + bi_call_bb = bi_call_in_edge0->dest; + join_tgt_in_edge_fall_thru = make_edge (guard_bb0, join_tgt_bb, + EDGE_FALSE_VALUE); + + bi_call_in_edge0->probability = REG_BR_PROB_BASE * ERR_PROB; + join_tgt_in_edge_fall_thru->probability = + REG_BR_PROB_BASE - bi_call_in_edge0->probability; + + /* Code generation for the rest of the conditions */ + guard_bb = guard_bb0; + while (nconds > 0) + { + unsigned ci0; + edge bi_call_in_edge; + block_stmt_iterator guard_bsi = bsi_for_stmt (cond_expr_start); + ci0 = ci; + cond_expr_start = VEC_index (tree, conds, ci0); + for (; ci < tn_cond_stmts; ci++) + { + tree c = VEC_index (tree, conds, ci); + gcc_assert (c || ci != ci0); + if (!c) + break; + bsi_insert_before (&guard_bsi, c, BSI_SAME_STMT); + cond_expr = c; + } + nconds--; + ci++; + gcc_assert (cond_expr && TREE_CODE (cond_expr) == COND_EXPR); + guard_bb_in_edge = split_block (guard_bb, cond_expr); + guard_bb_in_edge->flags &= ~EDGE_FALLTHRU; + guard_bb_in_edge->flags |= EDGE_FALSE_VALUE; + + bi_call_in_edge = make_edge (guard_bb, bi_call_bb, EDGE_TRUE_VALUE); + + bi_call_in_edge->probability = REG_BR_PROB_BASE * ERR_PROB; + guard_bb_in_edge->probability = + REG_BR_PROB_BASE - bi_call_in_edge->probability; + } + + VEC_free (tree, heap, conds); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + location_t loc; + loc = EXPR_LOCATION (bi_call); + fprintf (dump_file, + "%s:%d: note: function call is shrink-wrapped" + " into error conditions.\n", + LOCATION_FILE (loc), LOCATION_LINE (loc)); + } + + return true; +} + +/* The top level function for conditional dead code shrink + wrapping transformation. */ + +static bool +shrink_wrap_conditional_dead_built_in_calls (void) +{ + bool changed = false; + unsigned i = 0; + + unsigned n = VEC_length (tree, cond_dead_built_in_calls); + if (n == 0) + return false; + + for (; i < n ; i++) + { + tree bi_call = VEC_index (tree, cond_dead_built_in_calls, i); + changed |= shrink_wrap_one_built_in_call (bi_call); + } + + return changed; +} + +/* Pass entry points. */ + +static unsigned int +tree_call_cdce (void) +{ + basic_block bb; + block_stmt_iterator i; + bool something_changed = false; + cond_dead_built_in_calls = VEC_alloc (tree, heap, 64); + + FOR_EACH_BB (bb) + { + /* Collect dead call candidates. */ + for (i = bsi_start (bb); ! bsi_end_p (i); bsi_next (&i)) + { + tree stmt = bsi_stmt (i); + if (TREE_CODE (stmt) == CALL_EXPR + && is_call_dce_candidate (stmt)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Found conditional dead call: "); + print_generic_stmt (dump_file, stmt, TDF_SLIM); + fprintf (dump_file, "\n"); + } + VEC_quick_push (tree, cond_dead_built_in_calls, stmt); + } + } + } + + something_changed = + shrink_wrap_conditional_dead_built_in_calls (); + + VEC_free (tree, heap, cond_dead_built_in_calls); + + if (something_changed) + { + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + return (TODO_update_ssa | TODO_cleanup_cfg | TODO_ggc_collect + | TODO_remove_unused_locals); + } + else + return 0; +} + +static bool +gate_call_cdce (void) +{ + /* The limit constants used in the implementation + assume IEEE floating point format. Other formats + can be supported in the future if needed. */ + return flag_tree_builtin_call_dce != 0; +} + +struct gimple_opt_pass pass_call_cdce = +{ + { + GIMPLE_PASS, + "cdce", /* name */ + gate_call_cdce, /* gate */ + tree_call_cdce, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_TREE_CALL_CDCE, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_verify_ssa /* todo_flags_finish */ + } +}; diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 916df71ac6b..ea5aae069c1 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -337,6 +337,7 @@ extern struct gimple_opt_pass pass_dominator; extern struct gimple_opt_pass pass_dce; extern struct gimple_opt_pass pass_dce_loop; extern struct gimple_opt_pass pass_cd_dce; +extern struct gimple_opt_pass pass_call_cdce; extern struct gimple_opt_pass pass_merge_phi; extern struct gimple_opt_pass pass_split_crit_edges; extern struct gimple_opt_pass pass_pre; -- 2.30.2