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 <[email protected]>
* 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 <[email protected]>
* g++.dg/thread-ann/thread_annot_lock-82.C:
Expanded regression test
--
DeLesley Hutchins | Software Engineer | [email protected] | 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. */