>> +/* Return true if the use is defined by a gcov counter var. >> + It is used to check if an iv candidate is generated for >> + profiling. For profile generated ssa name, we should not >> + use it as IV because gcov counter may have data-race for >> + multithread program, it could involve tricky bug to use >> + such ssa var in IVOPT. >> + > > Add the snippets describing how ralloc introduces a second gcov > counter load and asynchronous update of it from another thread leading > to bogus trip count. > > Also mention that setting volatile flag on gcov counter accesses may > greatly degrade profile-gen performance. >
Comments added. >> + if (!is_gimple_assign (stmt)) >> + return false; >> + >> + rhs = gimple_assign_rhs1 (stmt); >> + if (TREE_CODE (rhs) != ARRAY_REF) >> + return false; >> + >> + decl = TREE_OPERAND (rhs, 0); >> + if (TREE_CODE (decl) != VAR_DECL) >> + return false; > > > > Also check TREE_STATIC and DECL_ARTIFICIAL flag. > > > David > Check added. Add DECL_ARTIFICIAL setting in build_var() in coverage.c. The updated patch is attached. Thanks, Wei.
2014-02-11 Wei Mi <w...@google.com> * tree-ssa-loop-ivopts.c (defined_by_gcov_counter): New. (contains_abnormal_ssa_name_p): Add defined_by_gcov_counter check for ssa name. * coverage.c (build_var): Set DECL_ARTIFICIAL(gcov var decl) to be 1. * testsuite/gcc.dg/profile-generate-4.c: New. Index: testsuite/gcc.dg/profile-generate-4.c =================================================================== --- testsuite/gcc.dg/profile-generate-4.c (revision 0) +++ testsuite/gcc.dg/profile-generate-4.c (revision 0) @@ -0,0 +1,21 @@ +/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-O2 -fprofile-generate -fno-tree-loop-im -fdump-tree-ivopts-details-blocks" } */ + +/* Because gcov counter related var has data race for multithread program, + compiler should prevent them from affecting program correctness. So + PROF_edge_counter variable should not be used as induction variable, or + else IVOPT may use such variable to compute loop boundary. */ + +void *ptr; +int N; + +void foo(void *t) { + int i; + for (i = 0; i < N; i++) { + t = *(void **)t; + } + ptr = t; +} + +/* { dg-final { scan-tree-dump-times "ssa name PROF_edge_counter" 0 "ivopts"} } */ +/* { dg-final { cleanup-tree-dump "ivopts" } } */ Index: coverage.c =================================================================== --- coverage.c (revision 207019) +++ coverage.c (working copy) @@ -1485,6 +1485,7 @@ build_var (tree fn_decl, tree type, int TREE_STATIC (var) = 1; TREE_ADDRESSABLE (var) = 1; DECL_ALIGN (var) = TYPE_ALIGN (type); + DECL_ARTIFICIAL (var) = 1; return var; } Index: tree-ssa-loop-ivopts.c =================================================================== --- tree-ssa-loop-ivopts.c (revision 207019) +++ tree-ssa-loop-ivopts.c (working copy) @@ -705,6 +705,115 @@ idx_contains_abnormal_ssa_name_p (tree b return !abnormal_ssa_name_p (*index); } +/* Return true if the use is defined by a gcov counter var. + It is used to check if an iv candidate is generated for + profiling. For profile generated ssa name, we should not + use it as IV because gcov counter may have data-race for + multithread program, it is compiler's responsibility to + avoid connecting profile counter related vars with program + correctness. + + Without the check, the following bug could happen in + following case: + * original loop + + int i; + for (i = 0; i < N; i++) { + t = *(void **)t; + } + + * after profile-gen and IVOPT, loop condition is replaced and + pretmp_1 is involved in loop boundary computation. + + pretmp_1 = __gcov0.foo[0]; + _22 = pretmp_1 + 1; + ... + _31 = (unsigned long) pretmp_1; + _32 = _30 + _31; + _33 = _32 + 2; + label: + ivtmp.8_9 = PHI <ivtmp.8_5(5), ivtmp.8_2(3)> + PROF_edge_counter_10 = (long int) ivtmp.8_9; + __gcov0.foo[0] = PROF_edge_counter_10; + ... + ivtmp.8_5 = ivtmp.8_9 + 1; + if (ivtmp.8_5 != _33) + goto label + + * after register allocation, pretmp_1 may be marked as REG_EQUIV in IRA + with __gcov0.foo[0] and some references are replaced by __gcov0.foo in LRA. + + _22 = __gcov0.foo[0] + 1; + ... + _31 = (unsigned long) __gcov0.foo[0]; + _32 = _30 + _31; + _33 = _32 + 2; + label: + .... + + * Bug happens when __gcov0.foo[0] is updated asynchronously by other thread + between the above __gcov0.foo[0] references statements. + + We don't choose to mark gcov counter as volatile because it may greatly + degrade profile-gen performance. + + To limit patterns to be checked, we list the possible cases + here: + Before PRE, the ssa name used to set __gcov counter is as + follows: + for () { + PROF_edge_counter_1 = __gcov.foo[i]; + PROF_edge_counter_2 = PROF_edge_counter_1 + 1; + __gcov.foo[i] = PROF_edge_counter_2; + } + If PRE works, the loop may be transformed to: + pretmp_1 = __gcov.foo[i]; + for () { + prephitmp_1 = PHI (PROF_edge_counter_2, pretmp_1); + PROF_edge_counter_1 = prephitmp_1; + PROF_edge_counter_2 = PROF_edge_counter_1 + 1; + __gcov.foo[i] = PROF_edge_counter_2; + } + So there are two cases: + case1: If PRE doesn't work, PROF_edge_counter_1 and PROF_edge_counter_2 + are neither induction variables candidates. We don't have to worry + about this case. + case2: If PRE works, the iv candidate base of PROF_edge_counter_1 and + PROF_edge_counter_2 are pretmp_1 or pretmp_1 + 1. pretmp_1 is defined + by __gcov var. + + So this func only has to check case2. For a ssa name which is an iv + candidate, check its base USE and see if it is defined by __gcov var. + Returning true means the ssa name is generated for profiling. */ + +bool +defined_by_gcov_counter (tree use) +{ + gimple stmt; + tree rhs, decl; + const char *name; + + stmt = SSA_NAME_DEF_STMT (use); + if (!is_gimple_assign (stmt)) + return false; + + rhs = gimple_assign_rhs1 (stmt); + if (TREE_CODE (rhs) != ARRAY_REF) + return false; + + decl = TREE_OPERAND (rhs, 0); + if (TREE_CODE (decl) != VAR_DECL + || !TREE_STATIC (decl) + || !DECL_ARTIFICIAL (decl)) + return false; + + name = IDENTIFIER_POINTER (DECL_NAME (decl)); + if (strncmp (name, "__gcov", 6)) + return false; + + return true; +} + /* Returns true if EXPR contains a ssa name that occurs in an abnormal phi node. */ @@ -721,7 +830,8 @@ contains_abnormal_ssa_name_p (tree expr) codeclass = TREE_CODE_CLASS (code); if (code == SSA_NAME) - return SSA_NAME_OCCURS_IN_ABNORMAL_PHI (expr) != 0; + return SSA_NAME_OCCURS_IN_ABNORMAL_PHI (expr) != 0 + || defined_by_gcov_counter (expr); if (code == INTEGER_CST || is_gimple_min_invariant (expr))