public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
From: j <j@lambda.is>
To: gcc@gcc.gnu.org
Subject: gccgo emits GIMPLE with temporaries for boolean expressions unlike gcc, gdc
Date: Wed, 3 Aug 2022 15:25:57 +0200	[thread overview]
Message-ID: <4ba7b262-ee0b-905f-ba0a-611ee6749184@lambda.is> (raw)


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

             reply	other threads:[~2022-08-03 13:26 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-03 13:25 j [this message]
2022-08-10 11:30 ` Martin Liška
2022-08-11  0:49 ` Ian Lance Taylor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4ba7b262-ee0b-905f-ba0a-611ee6749184@lambda.is \
    --to=j@lambda.is \
    --cc=gcc@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).