This is the mail archive of the mailing list for the GCC project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR 15159: CALL_PLACEHOLDERs and can_throw_internal

PR 15159 (reported against 3.4) is an ICE on code like:

    struct S { S (); };
    struct P { P (S *); };
    void foo (const P &);
    void bar ()
      P p = new S;
      foo (p);

It only triggers when sibcalls are enabled, and only then on targets
that allow sibcalls to external functions.  The root problem seems to be
confusion about whether a particular CALL_PLACEHOLDER can_throw_internal
or not.  Since CALL_PLACEHOLDERs have been removed from mainline (YEAH!!),
it's only a problem on the release branch.

On MIPS, and probably on other targets that allow external sibcalls,
the rtl expansion of bar() just contains two CALL_PLACEHOLDERs, one
for P::P() and one for foo().  Since the calls aren't wrapped in a try
block, and since there's no clean-up code to run, the calls are not
inside an EH region.

The problem is in the CALL_PLACEHOLDER for P::P().  Since S::S() can
potentially throw, "new S" is implemented using something like:

    void *mem = operator new (...);
        S::S[in charge] (...);
    catch (...)
        delete mem;
    ... use mem ...

This whole expression is embedded in the argument to P::P()'s CALL_EXPR
and gets expanded inside the two arms of its CALL_PLACEHOLDER.

Now can_throw_internal() says that a CALL_PLACEHOLDER can throw
internally if any insn inside it can.  But here we have a case in which
one insn in the placeholder can throw an exception to other insns in the
placeholder, but not to any insns outside the placeholder.  Thus, treated
as a unit, the placeholder _can't_ throw internally.

This causes confusion because calls that can throw internally should (of
course) end a basic block.  And at first, P::P() and foo() are indeed in
separate blocks.  But the P::P() placeholder itself isn't in an EH region,
so there are no EH edges leading out its block, just the fallthrough edge
to foo()'s block.  The CFG code therefore thinks it can merge them.
It later ICEs when it realises that a can_throw_internal() insn is
no longer at the end.

I suspect the most correct fix would be to teach can_throw_internal()
about this sort of case.  But given that the whole problem is moot on
mainline, I guess what we really want is the _safest_ fix.

Modifying can_throw_internal() seems a little dangerous (what corner
cases might I screw up?).  I think it would be better to expand the
try-catch block outside the CALL_PLACEHOLDER instead.  We can do that
by making unsafe_for_reeval() treat TRY_CATCH_EXPRs as "wildly unsafe"
for reevaluation.

Bootstrapped & regression tested on mips64el-linux-gnu.  OK for 3.4?


	PR rtl-optimization/15159
	* tree.c (unsafe_for_reeval): Return 2 for TRY_CATCH_EXPRs.

	* g++.dg/opt/placeholder1.C: New test.

Index: tree.c
RCS file: /cvs/gcc/gcc/gcc/tree.c,v
retrieving revision 1.342.2.3
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.342.2.3 tree.c
--- tree.c	5 Feb 2004 22:01:35 -0000	1.342.2.3
+++ tree.c	19 Jun 2004 09:10:02 -0000
@@ -1622,6 +1622,7 @@ unsafe_for_reeval (tree expr)
     case SAVE_EXPR:
     case RTL_EXPR:
+    case TRY_CATCH_EXPR:
       return 2;
     case TREE_LIST:
*** /dev/null	Fri Apr 23 00:21:55 2004
--- testsuite/g++.dg/opt/placeholder1.C	Sun Jun 20 09:11:11 2004
*** 0 ****
--- 1,10 ----
+ // PR rtl-optimization/15159
+ // { dg-options "-O2" }
+ struct S { S (); };
+ struct P { P (S *); };
+ void foo (const P &);
+ void bar ()
+ {
+   P p = new S;
+   foo (p);
+ }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]