public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc
@ 2022-08-03 13:25 j
  2022-08-10 11:30 ` Martin Liška
  2022-08-11  0:49 ` Ian Lance Taylor
  0 siblings, 2 replies; 3+ messages in thread
From: j @ 2022-08-03 13:25 UTC (permalink / raw)
  To: gcc


Hello,

I've proposed a patch [1] for condition coverage profiling in gcc, 
implemented in the middle-end alongside the branch coverage. I've 
written most of the tests for C and a few for C++ and finally got around 
to try it with a toy example for D and go and noticed something odd 
about Go's CFG construction.

abc.c:
     int fn (int a, int b, int c) {
         if (a && (b || c))
             return a;
         else
             return b * c;
     }

abc.d:
     int fn (int a, int b, int c) {
         if (a && (b || c))
             return a;
         else
             return b * c;
     }

abc.go:
     func fn (a int, b int, c int) int {
         a_ := a != 0;
         b_ := b != 0;
         c_ := c != 0;

         if a_ && (b_ || c_) {
             return 1;
         } else {
             return 0;
         }
     }

All were built with gcc --coverage -fprofile-conditions (my patch, but 
it does not affect this) and no optimization. The C and D programs 
behaved as expected:

gcov --conditions abc.d:

         3:    3:int fn (int a, int b, int c) {
        3*:    4:    if (a && (b || c))
conditions outcomes covered 3/6
condition  1 not covered (false)
condition  2 not covered (true)
condition  2 not covered (false)
         1:    5:        return a;
         -:    6:    else
         2:    7:        return b * c;


gcov --conditions abc.go:
         3:    5:func fn (a int, b int, c int) int {
         3:    6:        a_ := a != 0;
         3:    7:        b_ := b != 0;
         3:    8:        c_ := c != 0;
         -:    9:
        3*:   10:        if a_ && (b_ || c_) {
condition outcomes covered 2/2
condition outcomes covered 1/2
condition  0 not covered (true)
condition outcomes covered 2/2
         1:   11:            return 1;
         -:   12:        } else {
         2:   13:            return 0;
         -:   14:        }
         -:   15:}

So I dumped the gimple gcc -fdump-tree-gimple abc.go:

int main.fn (int a, int b, int c)
{
   int D.2725;
   int $ret0;

   $ret0 = 0;
   {
     bool a_;
     bool b_;
     bool c_;

     a_ = a != 0;
     b_ = b != 0;
     c_ = c != 0;
     {
       {
         GOTMP.0 = a_;
         if (GOTMP.0 != 0) goto <D.2719>; else goto <D.2720>;
         <D.2719>:
         {
           {
             GOTMP.1 = b_;
             _1 = ~GOTMP.1;
             if (_1 != 0) goto <D.2721>; else goto <D.2722>;
             <D.2721>:
             {
               GOTMP.1 = c_;
             }
             <D.2722>:
           }
           GOTMP.2 = GOTMP.1;
           GOTMP.0 = GOTMP.2;
         }
         <D.2720>:
       }
       if (GOTMP.0 != 0) goto <D.2723>; else goto <D.2724>;
       <D.2723>: 
 
 

       {
         {
           $ret0 = 1;
           D.2725 = $ret0;
           // predicted unlikely by early return (on trees) predictor.
           return D.2725;
         }
       }
       <D.2724>:
       {
         {
           $ret0 = 0;
           D.2725 = $ret0;
           // predicted unlikely by early return (on trees) predictor.
           return D.2725;
         }
       }
     }
   }
}

Where as D (and C) is more-or-less as you would expect:

int fn (int a, int b, int c) 
 
 

{
   int D.7895;

   if (a != 0) goto <D.7893>; else goto <D.7891>;
   <D.7893>:
   if (b != 0) goto <D.7892>; else goto <D.7894>;
   <D.7894>:
   if (c != 0) goto <D.7892>; else goto <D.7891>;
   <D.7892>:
   D.7895 = a;
   // predicted unlikely by early return (on trees) predictor.
   return D.7895;
   <D.7891>:
   D.7895 = b * c;
   // predicted unlikely by early return (on trees) predictor.
   return D.7895;
}

Clearly the decision inference algorithm is unable to properly 
instrument to Go program for condition coverage because of the use of 
temporaries in the emitted GIMPLE. The question is: is this intentional 
and/or required from Go's semantics or could it be considered a defect? 
Is emitting the GIMPLE without the use of temporaries feasible at all?

Thanks,
Jørgen

[1] https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598165.html

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc
  2022-08-03 13:25 gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc j
@ 2022-08-10 11:30 ` Martin Liška
  2022-08-11  0:49 ` Ian Lance Taylor
  1 sibling, 0 replies; 3+ messages in thread
From: Martin Liška @ 2022-08-10 11:30 UTC (permalink / raw)
  To: j, gcc; +Cc: Ian Lance Taylor

CCing Go maintainer.

Martin

On 8/3/22 15:25, j wrote:
> 
> Hello,
> 
> I've proposed a patch [1] for condition coverage profiling in gcc, implemented in the middle-end alongside the branch coverage. I've written most of the tests for C and a few for C++ and finally got around to try it with a toy example for D and go and noticed something odd about Go's CFG construction.
> 
> abc.c:
>     int fn (int a, int b, int c) {
>         if (a && (b || c))
>             return a;
>         else
>             return b * c;
>     }
> 
> abc.d:
>     int fn (int a, int b, int c) {
>         if (a && (b || c))
>             return a;
>         else
>             return b * c;
>     }
> 
> abc.go:
>     func fn (a int, b int, c int) int {
>         a_ := a != 0;
>         b_ := b != 0;
>         c_ := c != 0;
> 
>         if a_ && (b_ || c_) {
>             return 1;
>         } else {
>             return 0;
>         }
>     }
> 
> All were built with gcc --coverage -fprofile-conditions (my patch, but it does not affect this) and no optimization. The C and D programs behaved as expected:
> 
> gcov --conditions abc.d:
> 
>         3:    3:int fn (int a, int b, int c) {
>        3*:    4:    if (a && (b || c))
> conditions outcomes covered 3/6
> condition  1 not covered (false)
> condition  2 not covered (true)
> condition  2 not covered (false)
>         1:    5:        return a;
>         -:    6:    else
>         2:    7:        return b * c;
> 
> 
> gcov --conditions abc.go:
>         3:    5:func fn (a int, b int, c int) int {
>         3:    6:        a_ := a != 0;
>         3:    7:        b_ := b != 0;
>         3:    8:        c_ := c != 0;
>         -:    9:
>        3*:   10:        if a_ && (b_ || c_) {
> condition outcomes covered 2/2
> condition outcomes covered 1/2
> condition  0 not covered (true)
> condition outcomes covered 2/2
>         1:   11:            return 1;
>         -:   12:        } else {
>         2:   13:            return 0;
>         -:   14:        }
>         -:   15:}
> 
> So I dumped the gimple gcc -fdump-tree-gimple abc.go:
> 
> int main.fn (int a, int b, int c)
> {
>   int D.2725;
>   int $ret0;
> 
>   $ret0 = 0;
>   {
>     bool a_;
>     bool b_;
>     bool c_;
> 
>     a_ = a != 0;
>     b_ = b != 0;
>     c_ = c != 0;
>     {
>       {
>         GOTMP.0 = a_;
>         if (GOTMP.0 != 0) goto <D.2719>; else goto <D.2720>;
>         <D.2719>:
>         {
>           {
>             GOTMP.1 = b_;
>             _1 = ~GOTMP.1;
>             if (_1 != 0) goto <D.2721>; else goto <D.2722>;
>             <D.2721>:
>             {
>               GOTMP.1 = c_;
>             }
>             <D.2722>:
>           }
>           GOTMP.2 = GOTMP.1;
>           GOTMP.0 = GOTMP.2;
>         }
>         <D.2720>:
>       }
>       if (GOTMP.0 != 0) goto <D.2723>; else goto <D.2724>;
>       <D.2723>:
> 
> 
>       {
>         {
>           $ret0 = 1;
>           D.2725 = $ret0;
>           // predicted unlikely by early return (on trees) predictor.
>           return D.2725;
>         }
>       }
>       <D.2724>:
>       {
>         {
>           $ret0 = 0;
>           D.2725 = $ret0;
>           // predicted unlikely by early return (on trees) predictor.
>           return D.2725;
>         }
>       }
>     }
>   }
> }
> 
> Where as D (and C) is more-or-less as you would expect:
> 
> int fn (int a, int b, int c)
> 
> 
> {
>   int D.7895;
> 
>   if (a != 0) goto <D.7893>; else goto <D.7891>;
>   <D.7893>:
>   if (b != 0) goto <D.7892>; else goto <D.7894>;
>   <D.7894>:
>   if (c != 0) goto <D.7892>; else goto <D.7891>;
>   <D.7892>:
>   D.7895 = a;
>   // predicted unlikely by early return (on trees) predictor.
>   return D.7895;
>   <D.7891>:
>   D.7895 = b * c;
>   // predicted unlikely by early return (on trees) predictor.
>   return D.7895;
> }
> 
> Clearly the decision inference algorithm is unable to properly instrument to Go program for condition coverage because of the use of temporaries in the emitted GIMPLE. The question is: is this intentional and/or required from Go's semantics or could it be considered a defect? Is emitting the GIMPLE without the use of temporaries feasible at all?
> 
> Thanks,
> Jørgen
> 
> [1] https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598165.html


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc
  2022-08-03 13:25 gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc j
  2022-08-10 11:30 ` Martin Liška
@ 2022-08-11  0:49 ` Ian Lance Taylor
  1 sibling, 0 replies; 3+ messages in thread
From: Ian Lance Taylor @ 2022-08-11  0:49 UTC (permalink / raw)
  To: j; +Cc: gcc

On Wed, Aug 3, 2022 at 6:26 AM j <j@lambda.is> wrote:
>
> I've proposed a patch [1] for condition coverage profiling in gcc,
> implemented in the middle-end alongside the branch coverage. I've
> written most of the tests for C and a few for C++ and finally got around
> to try it with a toy example for D and go and noticed something odd
> about Go's CFG construction.
>
> abc.c:
>      int fn (int a, int b, int c) {
>          if (a && (b || c))
>              return a;
>          else
>              return b * c;
>      }
>
> abc.d:
>      int fn (int a, int b, int c) {
>          if (a && (b || c))
>              return a;
>          else
>              return b * c;
>      }
>
> abc.go:
>      func fn (a int, b int, c int) int {
>          a_ := a != 0;
>          b_ := b != 0;
>          c_ := c != 0;
>
>          if a_ && (b_ || c_) {
>              return 1;
>          } else {
>              return 0;
>          }
>      }
>
> All were built with gcc --coverage -fprofile-conditions (my patch, but
> it does not affect this) and no optimization. The C and D programs
> behaved as expected:
>
> gcov --conditions abc.d:
>
>          3:    3:int fn (int a, int b, int c) {
>         3*:    4:    if (a && (b || c))
> conditions outcomes covered 3/6
> condition  1 not covered (false)
> condition  2 not covered (true)
> condition  2 not covered (false)
>          1:    5:        return a;
>          -:    6:    else
>          2:    7:        return b * c;
>
>
> gcov --conditions abc.go:
>          3:    5:func fn (a int, b int, c int) int {
>          3:    6:        a_ := a != 0;
>          3:    7:        b_ := b != 0;
>          3:    8:        c_ := c != 0;
>          -:    9:
>         3*:   10:        if a_ && (b_ || c_) {
> condition outcomes covered 2/2
> condition outcomes covered 1/2
> condition  0 not covered (true)
> condition outcomes covered 2/2
>          1:   11:            return 1;
>          -:   12:        } else {
>          2:   13:            return 0;
>          -:   14:        }
>          -:   15:}
>
> So I dumped the gimple gcc -fdump-tree-gimple abc.go:
>
> int main.fn (int a, int b, int c)
> {
>    int D.2725;
>    int $ret0;
>
>    $ret0 = 0;
>    {
>      bool a_;
>      bool b_;
>      bool c_;
>
>      a_ = a != 0;
>      b_ = b != 0;
>      c_ = c != 0;
>      {
>        {
>          GOTMP.0 = a_;
>          if (GOTMP.0 != 0) goto <D.2719>; else goto <D.2720>;
>          <D.2719>:
>          {
>            {
>              GOTMP.1 = b_;
>              _1 = ~GOTMP.1;
>              if (_1 != 0) goto <D.2721>; else goto <D.2722>;
>              <D.2721>:
>              {
>                GOTMP.1 = c_;
>              }
>              <D.2722>:
>            }
>            GOTMP.2 = GOTMP.1;
>            GOTMP.0 = GOTMP.2;
>          }
>          <D.2720>:
>        }
>        if (GOTMP.0 != 0) goto <D.2723>; else goto <D.2724>;
>        <D.2723>:
>
>
>
>        {
>          {
>            $ret0 = 1;
>            D.2725 = $ret0;
>            // predicted unlikely by early return (on trees) predictor.
>            return D.2725;
>          }
>        }
>        <D.2724>:
>        {
>          {
>            $ret0 = 0;
>            D.2725 = $ret0;
>            // predicted unlikely by early return (on trees) predictor.
>            return D.2725;
>          }
>        }
>      }
>    }
> }
>
> Where as D (and C) is more-or-less as you would expect:
>
> int fn (int a, int b, int c)
>
>
>
> {
>    int D.7895;
>
>    if (a != 0) goto <D.7893>; else goto <D.7891>;
>    <D.7893>:
>    if (b != 0) goto <D.7892>; else goto <D.7894>;
>    <D.7894>:
>    if (c != 0) goto <D.7892>; else goto <D.7891>;
>    <D.7892>:
>    D.7895 = a;
>    // predicted unlikely by early return (on trees) predictor.
>    return D.7895;
>    <D.7891>:
>    D.7895 = b * c;
>    // predicted unlikely by early return (on trees) predictor.
>    return D.7895;
> }
>
> Clearly the decision inference algorithm is unable to properly
> instrument to Go program for condition coverage because of the use of
> temporaries in the emitted GIMPLE. The question is: is this intentional
> and/or required from Go's semantics or could it be considered a defect?
> Is emitting the GIMPLE without the use of temporaries feasible at all?

The Go frontend converts && and || expressions into code that uses
explicit if statements.  This is done largely as an internal
simplification.  Go has rules about the order in which function calls
and certain other kinds of expressions must be evaluated.  Separating
out the order of evaluation imposed by && and || simplifies the
implementation of the other order of evaluation rules.

Ian

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-08-11  0:50 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-03 13:25 gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc j
2022-08-10 11:30 ` Martin Liška
2022-08-11  0:49 ` Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).