Bug 66512 - PRE fails to optimize calls to pure functions in C++, ok in C
Summary: PRE fails to optimize calls to pure functions in C++, ok in C
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-06-11 15:06 UTC by Alexander Monakov
Modified: 2020-11-24 23:44 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Monakov 2015-06-11 15:06:06 UTC
The following testcase:

int p(void) __attribute__((const));
void g(int);
void f()
{
  for (;;)
    g(p());
}

is optimized when compiled as C:

f:
        pushq   %rbx
        call    p
        movl    %eax, %ebx
.L2:
        movl    %ebx, %edi
        call    g
        jmp     .L2

and not optimized when compiled as C++:

_Z1fv:
        subq    $8, %rsp
.L2:
        call    _Z1pv
        movl    %eax, %edi
        call    _Z1gi
        jmp     .L2

For C the motion is performed by the PRE pass; tree dumps for PRE diverge after:

@@ -95,76 +95,52 @@ Value numbering .MEM_4 stmt = g (_3);
 Setting value number of .MEM_4 to .MEM_4
 Processing SCC needed 3 iterations
 Value numbers:
[...]
 avail_out[2] := {  }
-exp_gen[3] := { {call_expr<p>} (0003) }
+exp_gen[3] := {  }
 phi_gen[3] := {  }
Comment 1 Richard Biener 2015-06-14 10:36:37 UTC
Because p may throw.  What we miss here is the fact that it should only matter
if p throws internally for IL consistency.  Of course it still matters for
observing other side-effects if p throws and after the transform now does so
before side-effects that should be observed otherwise.  Consider


 for (;;)
   {
     printf("foo");
     g(p())
   }

and p throwing.

So you miss a nothrow attribute or a throw() specification here.  const
does _not_ imply nothrow.
Comment 2 Alexander Monakov 2015-06-15 10:33:19 UTC
In that case I'd like to contribute a documentation patch to make that clear in the pure/const attribute information, but I need more explanation.  I see that

int p(void) __attribute__((const));
void f()
{
  p();
  p();
}

is optimized to an empty function, even though p may throw.  Is that not a bug?

Also, could you please expand your explanation in the first paragraph, i.e. this:

  What we miss here is the fact that it should only matter
  if p throws internally for IL consistency.  Of course it
  still matters for observing other side-effects if p throws
  and after the transform now does so before side-effects
  that should be observed otherwise.

I'm probably missing a lot of contextual knowledge to understand that.  TIA.
Comment 3 Richard Biener 2015-06-18 11:15:33 UTC
(In reply to Alexander Monakov from comment #2)
> In that case I'd like to contribute a documentation patch to make that clear
> in the pure/const attribute information, but I need more explanation.  I see
> that
> 
> int p(void) __attribute__((const));
> void f()
> {
>   p();
>   p();
> }
> 
> is optimized to an empty function, even though p may throw.  Is that not a
> bug?

I would say so.  But this has quite some implementation issues, also with...

> Also, could you please expand your explanation in the first paragraph, i.e.
> this:
> 
>   What we miss here is the fact that it should only matter
>   if p throws internally for IL consistency.  Of course it
>   still matters for observing other side-effects if p throws
>   and after the transform now does so before side-effects
>   that should be observed otherwise.
> 
> I'm probably missing a lot of contextual knowledge to understand that.  TIA.

... this, the side-effect ordering of stores and the throwing p() call is
not properly represented.
Comment 4 Fangrui Song 2020-11-24 23:44:41 UTC
Should this be reopened?

https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html 'const' is not clarified on its interaction with threads (https://gcc.gnu.org/legacy-ml/gcc/2015-09/msg00365.html)

and 

void f()
{
  for (;;)
    g(p());
}

is still pessimized for C++ (I tend to agree that 'const' should imply 'nothrow'; even if no, the #c2 case should be resolved properly)