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" } }