From 4028020e9f517e272f3f9889d35f74cac6bbbb29 Mon Sep 17 00:00:00 2001 From: davidxl Date: Fri, 15 Oct 2010 23:16:59 +0000 Subject: [PATCH] uninit var analysis enhancement git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@165530 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 7 ++ gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.dg/uninit-pred-3_a.C | 77 ++++++++++++++ gcc/testsuite/g++.dg/uninit-pred-3_b.C | 87 +++++++++++++++ gcc/tree-ssa-uninit.c | 187 +++++++++++++++++++++++++-------- 5 files changed, 321 insertions(+), 42 deletions(-) create mode 100644 gcc/testsuite/g++.dg/uninit-pred-3_a.C create mode 100644 gcc/testsuite/g++.dg/uninit-pred-3_b.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0d3d9b26ef3..d7ffbbb412f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2010-10-15 Xinliang David Li + + * tree-ssa-uninit.c (prune_uninit_phi_opnds_in_unrealizable_paths): New + function. + (use_pred_not_overlap_with_undef_path_pred): Outline phi arg pruning + into a recursive function. + 2010-10-15 Uros Bizjak * config/i386/i386.md (*movdfcc_1_rex64): Correct mode attribute. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 43f2aa0b45d..bc57cde8938 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2010-10-15 Xinliang David Li + + * g++.dg/uninit-pred-3_a.C: New test. + * g++.dg/uninit-pred-3_b.C: New test. + 2010-10-15 Nicola Pero * objc.dg/gnu-api-2-object.m: New. diff --git a/gcc/testsuite/g++.dg/uninit-pred-3_a.C b/gcc/testsuite/g++.dg/uninit-pred-3_a.C new file mode 100644 index 00000000000..91014079004 --- /dev/null +++ b/gcc/testsuite/g++.dg/uninit-pred-3_a.C @@ -0,0 +1,77 @@ +/* { dg-do compile } */ +/* { dg-options "-Wuninitialized -O2" } */ + +/* Multiple initialization paths. */ + +typedef long long int64; +void incr (); +bool is_valid (int); +int get_time (); + +class A +{ +public: + A (); + ~A () { + if (I) delete I; + } + +private: + int* I; +}; + +bool get_url (A *); +bool get_url2 (A *); + +class M { + + public: + __attribute__ ((always_inline)) + bool GetC (int *c) { + + A details_str; + /* Intialization path 1 */ + if (get_url (&details_str)) + { + *c = get_time (); + return true; + } + + /* insert dtor calls (inlined) into following return paths */ + A tmp_str; + + /* Intializtion path 2 */ + if (get_url2 (&details_str)) + { + *c = get_time (); + return true; + } + + return false; + } + + void do_sth(); + void do_sth2(); + + void P (int64 t) + { + int cc; + if (!GetC (&cc)) /* return flag checked properly */ + return; + + if (cc <= 0) /* { dg-bogus "uninitialized" "uninitialized variable warning" } */ + { + this->do_sth(); + return; + } + + do_sth2(); + } +}; + +M* m; +void test(int x) +{ + m = new M; + m->P(x); +} diff --git a/gcc/testsuite/g++.dg/uninit-pred-3_b.C b/gcc/testsuite/g++.dg/uninit-pred-3_b.C new file mode 100644 index 00000000000..cfe2113bb6e --- /dev/null +++ b/gcc/testsuite/g++.dg/uninit-pred-3_b.C @@ -0,0 +1,87 @@ +/* { dg-do compile } */ +/* { dg-options "-Wuninitialized -O2" } */ + +/* Multiple initialization paths. */ + +typedef long long int64; +void incr (); +bool is_valid (int); +int get_time (); + +class A +{ +public: + A (); + ~A () { + if (I) delete I; + } + +private: + int* I; +}; + +bool get_url (A *); +bool get_url2 (A *); +bool get_url3 (A *); + +class M { + + public: + __attribute__ ((always_inline)) + bool GetC (int *c) { + + A details_str; + + /* Initialization path 1 */ + if (get_url (&details_str)) + { + *c = get_time (); + return true; + } + + /* Destructor call before return*/ + A tmp_str; + + /* Initialization path 2 */ + if (get_url2 (&details_str)) + { + *c = get_time (); + return true; + } + + /* Fail to initialize in this path but + still returns true */ + if (get_url2 (&details_str)) + { + /* Fail to initialize *c */ + return true; + } + + return false; + } + + void do_sth(); + void do_sth2(); + + void P (int64 t) + { + int cc; /* { dg-excess-errors "note: 'cc' was declared here" } */ + if (!GetC (&cc)) + return; + + if (cc <= 0) /* { dg-warning "uninitialized" "uninitialized variable warning" } */ + { + this->do_sth(); + return; + } + + do_sth2(); + } +}; + +M* m; +void test(int x) +{ + m = new M; + m->P(x); +} diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c index 78b88e9bd93..01ff43165d3 100644 --- a/gcc/tree-ssa-uninit.c +++ b/gcc/tree-ssa-uninit.c @@ -785,6 +785,139 @@ is_use_properly_guarded (gimple use_stmt, unsigned uninit_opnds, struct pointer_set_t *visited_phis); +/* Returns true if all uninitialized opnds are pruned. Returns false + otherwise. PHI is the phi node with uninitialized operands, + UNINIT_OPNDS is the bitmap of the uninitialize operand positions, + FLAG_DEF is the statement defining the flag guarding the use of the + PHI output, BOUNDARY_CST is the const value used in the predicate + associated with the flag, CMP_CODE is the comparison code used in + the predicate, VISITED_PHIS is the pointer set of phis visited, and + VISITED_FLAG_PHIS is the pointer to the pointer set of flag definitions + that are also phis. + + Example scenario: + + BB1: + flag_1 = phi <0, 1> // (1) + var_1 = phi + + + BB2: + flag_2 = phi <0, flag_1, flag_1> // (2) + var_2 = phi + if (flag_2 == 1) + goto BB3; + + BB3: + use of var_2 // (3) + + Because some flag arg in (1) is not constant, if we do not look into the + flag phis recursively, it is conservatively treated as unknown and var_1 + is thought to be flowed into use at (3). Since var_1 is potentially uninitialized + a false warning will be emitted. Checking recursively into (1), the compiler can + find out that only some_val (which is defined) can flow into (3) which is OK. + +*/ + +static bool +prune_uninit_phi_opnds_in_unrealizable_paths ( + gimple phi, unsigned uninit_opnds, + gimple flag_def, tree boundary_cst, + enum tree_code cmp_code, + struct pointer_set_t *visited_phis, + bitmap *visited_flag_phis) +{ + unsigned i; + + for (i = 0; i < MIN (32, gimple_phi_num_args (flag_def)); i++) + { + tree flag_arg; + + if (!MASK_TEST_BIT (uninit_opnds, i)) + continue; + + flag_arg = gimple_phi_arg_def (flag_def, i); + if (!is_gimple_constant (flag_arg)) + { + gimple flag_arg_def, phi_arg_def; + tree phi_arg; + unsigned uninit_opnds_arg_phi; + + if (TREE_CODE (flag_arg) != SSA_NAME) + return false; + flag_arg_def = SSA_NAME_DEF_STMT (flag_arg); + if (gimple_code (flag_arg_def) != GIMPLE_PHI) + return false; + + phi_arg = gimple_phi_arg_def (phi, i); + if (TREE_CODE (phi_arg) != SSA_NAME) + return false; + + phi_arg_def = SSA_NAME_DEF_STMT (phi_arg); + if (gimple_code (phi_arg_def) != GIMPLE_PHI) + return false; + + if (gimple_bb (phi_arg_def) != gimple_bb (flag_arg_def)) + return false; + + if (!*visited_flag_phis) + *visited_flag_phis = BITMAP_ALLOC (NULL); + + if (bitmap_bit_p (*visited_flag_phis, + SSA_NAME_VERSION (gimple_phi_result (flag_arg_def)))) + return false; + + bitmap_set_bit (*visited_flag_phis, + SSA_NAME_VERSION (gimple_phi_result (flag_arg_def))); + + /* Now recursively prune the uninitialized phi args. */ + uninit_opnds_arg_phi = compute_uninit_opnds_pos (phi_arg_def); + if (!prune_uninit_phi_opnds_in_unrealizable_paths ( + phi_arg_def, uninit_opnds_arg_phi, + flag_arg_def, boundary_cst, cmp_code, + visited_phis, visited_flag_phis)) + return false; + + bitmap_clear_bit (*visited_flag_phis, + SSA_NAME_VERSION (gimple_phi_result (flag_arg_def))); + continue; + } + + /* Now check if the constant is in the guarded range. */ + if (is_value_included_in (flag_arg, boundary_cst, cmp_code)) + { + tree opnd; + gimple opnd_def; + + /* Now that we know that this undefined edge is not + pruned. If the operand is defined by another phi, + we can further prune the incoming edges of that + phi by checking the predicates of this operands. */ + + opnd = gimple_phi_arg_def (phi, i); + opnd_def = SSA_NAME_DEF_STMT (opnd); + if (gimple_code (opnd_def) == GIMPLE_PHI) + { + edge opnd_edge; + unsigned uninit_opnds2 + = compute_uninit_opnds_pos (opnd_def); + gcc_assert (!MASK_EMPTY (uninit_opnds2)); + opnd_edge = gimple_phi_arg_edge (phi, i); + if (!is_use_properly_guarded (phi, + opnd_edge->src, + opnd_def, + uninit_opnds2, + visited_phis)) + return false; + } + else + return false; + } + } + + return true; +} + /* A helper function that determines if the predicate set of the use is not overlapping with that of the uninit paths. The most common senario of guarded use is in Example 1: @@ -873,6 +1006,8 @@ use_pred_not_overlap_with_undef_path_pred ( bool swap_cond = false; bool invert = false; VEC(use_pred_info_t, heap) *the_pred_chain; + bitmap visited_flag_phis = NULL; + bool all_pruned = false; gcc_assert (num_preds > 0); /* Find within the common prefix of multiple predicate chains @@ -935,50 +1070,18 @@ use_pred_not_overlap_with_undef_path_pred ( if (cmp_code == ERROR_MARK) return false; - for (i = 0; i < sizeof (unsigned); i++) - { - tree flag_arg; - - if (!MASK_TEST_BIT (uninit_opnds, i)) - continue; - - flag_arg = gimple_phi_arg_def (flag_def, i); - if (!is_gimple_constant (flag_arg)) - return false; - - /* Now check if the constant is in the guarded range. */ - if (is_value_included_in (flag_arg, boundary_cst, cmp_code)) - { - tree opnd; - gimple opnd_def; - - /* Now that we know that this undefined edge is not - pruned. If the operand is defined by another phi, - we can further prune the incoming edges of that - phi by checking the predicates of this operands. */ + all_pruned = prune_uninit_phi_opnds_in_unrealizable_paths (phi, + uninit_opnds, + flag_def, + boundary_cst, + cmp_code, + visited_phis, + &visited_flag_phis); - opnd = gimple_phi_arg_def (phi, i); - opnd_def = SSA_NAME_DEF_STMT (opnd); - if (gimple_code (opnd_def) == GIMPLE_PHI) - { - edge opnd_edge; - unsigned uninit_opnds2 - = compute_uninit_opnds_pos (opnd_def); - gcc_assert (!MASK_EMPTY (uninit_opnds2)); - opnd_edge = gimple_phi_arg_edge (phi, i); - if (!is_use_properly_guarded (phi, - opnd_edge->src, - opnd_def, - uninit_opnds2, - visited_phis)) - return false; - } - else - return false; - } - } + if (visited_flag_phis) + BITMAP_FREE (visited_flag_phis); - return true; + return all_pruned; } /* Returns true if TC is AND or OR */ -- 2.11.0