Let's try this again; perhaps I should learn to use e-mail. :-) This patch fixes an ICE caused when the ipa-sra optimization deletes function arguments that are referenced from within a thread safety attribute. It will attempt to detect this situation and recover gracefully. Since a graceful recovery is not always possible, an optional warning (-Wwarn-thread-optimization) can be turned on to inform the user that certain attributes have been removed by optimization.
Bootstrapped and passed gcc regression testsuite on x86_64-unknown-linux-gnu. Okay for google/gcc-4_6? -DeLesley Changelog.google-4_6: 2011-11-02 DeLesley Hutchins <deles...@google.com> * tree-threadsafe-analyze.c: Ignores invalid attributes, issues a warning, recovers gracefully. * common.opt: Adds new thread safety warning. testsuite/Changelog.google-4_6: 2011-11-02 DeLesley Hutchins <deles...@google.com> * g++.dg/thread-ann/thread_annot_lock-82.C: Expanded regression test -- DeLesley Hutchins | Software Engineer | deles...@google.com | 505-206-0315
Index: testsuite/g++.dg/thread-ann/thread_annot_lock-82.C =================================================================== --- testsuite/g++.dg/thread-ann/thread_annot_lock-82.C (revision 180716) +++ testsuite/g++.dg/thread-ann/thread_annot_lock-82.C (working copy) @@ -1,7 +1,7 @@ -// Test template methods in the presence of cloned constructors. -// Regression test for bugfix. +// Regression tests: fix ICE issues when IPA-SRA deletes formal +// function parameters. // { dg-do compile } -// { dg-options "-Wthread-safety -O3" } +// { dg-options "-Wthread-safety -Wthread-warn-optimization -O3" } #include "thread_annot_common.h" @@ -10,6 +10,7 @@ void do_something(void* a); class Foo { Mutex mu_; + int a GUARDED_BY(mu_); // with optimization turned on, ipa-sra should eliminate the hidden // "this" argument, thus invalidating EXCLUSIVE_LOCKS_REQUIRED. @@ -18,6 +19,7 @@ class Foo { } void foo(Foo* f); + void bar(); }; void Foo::foo(Foo* f) { @@ -28,3 +30,17 @@ void Foo::foo(Foo* f) { mu_.Unlock(); } + +class SCOPED_LOCKABLE DummyMutexLock { +public: + // IPA-SRA should kill the parameters to these functions + explicit DummyMutexLock(Mutex* mutex) EXCLUSIVE_LOCK_FUNCTION(mutex) {} + ~DummyMutexLock() UNLOCK_FUNCTION() {} +}; + + +void Foo::bar() { + // Matches two warnings: + DummyMutexLock dlock(&mu_); // { dg-warning "attribute has been removed by optimization." } + a = 1; // warning here should be suppressed, due to errors handling dlock +} Index: common.opt =================================================================== --- common.opt (revision 180716) +++ common.opt (working copy) @@ -680,6 +680,10 @@ Wthread-attr-bind-param Common Var(warn_thread_attr_bind_param) Init(1) Warning Make the thread safety analysis try to bind the function parameters used in the attributes +Wthread-warn-optimization +Common Var(warn_thread_optimization) Init(0) Warning +Warn when optimizations invalidate the thread safety analysis. + Wtype-limits Common Var(warn_type_limits) Init(-1) Warning Warn if a comparison is always true or always false due to the limited range of the data type Index: tree-threadsafe-analyze.c =================================================================== --- tree-threadsafe-analyze.c (revision 180716) +++ tree-threadsafe-analyze.c (working copy) @@ -1594,7 +1594,10 @@ get_actual_argument_from_position (gimple call, tr lock_pos = TREE_INT_CST_LOW (pos_arg); - gcc_assert (lock_pos >= 1 && lock_pos <= num_args); + /* The ipa-sra optimization can occasionally delete arguments, thus + invalidating the index. */ + if (lock_pos < 1 || lock_pos > num_args) + return NULL_TREE; /* The lock position specified in the attributes is 1-based, so we need to subtract 1 from it when accessing the call arguments. */ @@ -1734,7 +1737,27 @@ handle_lock_primitive_attrs (gimple call, tree fde a formal parameter, we need to grab the corresponding actual argument of the call. */ else if (TREE_CODE (arg) == INTEGER_CST) - arg = get_actual_argument_from_position (call, arg); + { + arg = get_actual_argument_from_position (call, arg); + /* If ipa-sra has rewritten the call, then recover as gracefully as + possible. */ + if (!arg) + { + /* Add the universal lock to the lockset to suppress subsequent + errors. */ + if (is_exclusive_lock) + pointer_set_insert(live_excl_locks, error_mark_node); + else + pointer_set_insert(live_shared_locks, error_mark_node); + + if (warn_thread_optimization) + warning_at (*locus, OPT_Wthread_safety, + G_("Lock attribute has been removed by " + "optimization.")); + + return; + } + } else if (TREE_CODE (get_leftmost_base_var (arg)) == PARM_DECL) { tree new_base @@ -1890,7 +1913,18 @@ handle_unlock_primitive_attr (gimple call, tree fd a formal parameter, we need to grab the corresponding actual argument of the call. */ if (TREE_CODE (arg) == INTEGER_CST) - lockable = get_actual_argument_from_position (call, arg); + { + lockable = get_actual_argument_from_position (call, arg); + /* If ipa-sra has rewritten the call, then fail as gracefully as + possible -- just leave the lock in the lockset. */ + if (!lockable) { + if (warn_thread_optimization) + warning_at (*locus, OPT_Wthread_safety, + G_("Unlock attribute has been removed by " + "optimization.")); + return; + } + } else if (TREE_CODE (get_leftmost_base_var (arg)) == PARM_DECL) { tree fdecl = gimple_call_fndecl (call); @@ -1908,7 +1942,15 @@ handle_unlock_primitive_attr (gimple call, tree fd } else { - gcc_assert (base_obj); + /* If ipa-sra has killed arguments, then base_obj may be NULL. + Attempt to recover gracefully by leaving the lock in the lockset. */ + if (!base_obj) { + if (warn_thread_optimization) + warning_at (*locus, OPT_Wthread_safety, + G_("Unlock attribute has been removed by " + "optimization.")); + return; + } /* Check if the primitive is an unlock routine (e.g. the destructor or a release function) of a scoped_lock. If so, get the lock that is @@ -2099,6 +2141,9 @@ handle_function_lock_requirement (gimple call, tre else if (TREE_CODE (lock) == INTEGER_CST) { lock = get_actual_argument_from_position (call, lock); + /* Ignore attribute if ipa-sra has killed the argument. */ + if (!lock) + return; /* If the lock is a function argument, we don't want to prepend the base object to the lock name. Set the TMP_BASE_OBJ to NULL. */