Hi,
when compiling:
struct A
{
virtual int foo (void) {return 42;}
};
int test (void)
{
struct A a, *b=&a;
return b->foo();
}
We wind up the following useless EH:
int test() ()
{
int D.2235;
int (*__vtbl_ptr_type) () * D.2236;
int (*__vtbl_ptr_type) () D.2237;
struct A a;
struct A * b;
try
{
A::A (&a);
b = &a;
D.2236 = b->_vptr.A;
D.2237 = *D.2236;
D.2235 = OBJ_TYPE_REF(D.2237;b->0) (b);
return D.2235;
}
finally
{
a = {CLOBBER};
}
}
while ehcleanup gets rid of the try..finally code later, it happens only
with optimizing compilation. With -O0 the EH gets all the way down to the
binary that is rather embarassing.
I believe this is a regression since CLOBBERs were introduced.
Gimplifier already has logic to skip empty cleanups, the patch bellow
just makes it to ignore clobbers.
I checked that empty_body_p is used only in gomp lowering in very similar
scenario, so I think it is safe to change them and we don't need to introduce
new statement.
On related note however, the ehcleanup pass itself is using nondebug_stmt
to detect empty cleanups. Again I think it is bug. I wonder if we should
not invent active_stmt (matching RTL's naming scheme) and revisit current
uses of non-debug?
Bootstrapped/regtested x86_64-linux, OK?
* gimplify.c (gimplify_bind_expr, gimplify_expr): Use empty_body_p
to detect no-op sequences.
* gimple.c (empty_body_p): Ignore clobbers.
* g++.dg/tree-ssa/ehcleanup-1.C: Update testcase so it is harder
to optimize
* g++.dg/tree-ssa/ehcleanup-2.C: New testcase.
Index: gimplify.c
===================================================================
--- gimplify.c (revision 206684)
+++ gimplify.c (working copy)
@@ -1106,7 +1106,7 @@
}
}
- if (cleanup)
+ if (!empty_body_p (cleanup))
{
gimple gs;
gimple_seq new_body;
@@ -7771,7 +7771,7 @@
gimplify_and_add (TREE_OPERAND (*expr_p, 0), &eval);
gimplify_and_add (TREE_OPERAND (*expr_p, 1), &cleanup);
/* Don't create bogus GIMPLE_TRY with empty cleanup. */
- if (gimple_seq_empty_p (cleanup))
+ if (empty_body_p (cleanup))
{
gimple_seq_add_seq (pre_p, eval);
ret = GS_ALL_DONE;
Index: gimple.c
===================================================================
--- gimple.c (revision 206684)
+++ gimple.c (working copy)
@@ -1264,6 +1264,7 @@
return true;
for (i = gsi_start (body); !gsi_end_p (i); gsi_next (&i))
if (!empty_stmt_p (gsi_stmt (i))
+ && !gimple_clobber_p (gsi_stmt (i))
&& !is_gimple_debug (gsi_stmt (i)))
return false;
Index: testsuite/g++.dg/tree-ssa/ehcleanup-1.C
===================================================================
--- testsuite/g++.dg/tree-ssa/ehcleanup-1.C (revision 206684)
+++ testsuite/g++.dg/tree-ssa/ehcleanup-1.C (working copy)
@@ -12,7 +12,8 @@
public:
~a () NOEXCEPT_FALSE
{
- if (0)
+ int t = 0;
+ if (t)
can_throw ();
}
};
@@ -23,9 +24,9 @@
can_throw ();
}
// We ought to remove implicit cleanup, since destructor is empty.
-// { dg-final { scan-tree-dump-times "Empty EH handler" 2 "ehcleanup1" } }
+// { dg-final { scan-tree-dump-times "Empty EH handler" 1 "ehcleanup1" } }
//
// And as a result also contained control flow.
-// { dg-final { scan-tree-dump-times "Removing unreachable" 6 "ehcleanup1" } }
+// { dg-final { scan-tree-dump-times "Removing unreachable" 2 "ehcleanup1" } }
//
// { dg-final { cleanup-tree-dump "ehcleanup1" } }
Index: testsuite/g++.dg/tree-ssa/ehcleanup-2.C
===================================================================
--- testsuite/g++.dg/tree-ssa/ehcleanup-2.C (revision 0)
+++ testsuite/g++.dg/tree-ssa/ehcleanup-2.C (revision 0)
@@ -0,0 +1,12 @@
+// { dg-options "-O0 -fdump-tree-gimple" }
+ struct A
+ {
+ virtual int foo (void) {return 42;}
+ };
+int test (void)
+{
+ struct A a, *b=&a;
+ return b->foo();
+}
+// { dg-final { scan-tree-dump-not "finally" "gimple" } }
+// { dg-final { cleanup-tree-dump "gimple" } }