https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67644

            Bug ID: 67644
           Summary: [SH] double load on volatile bitfield mem
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: olegendo at gcc dot gnu.org
  Target Milestone: ---
            Target: sh*-*-*

The following:

struct USRSTR
{
  union
  {
    unsigned char BYTE;
    struct
    {
      unsigned char BIT7:1;
      unsigned char BIT6:1;
      unsigned char BIT5:1;
      unsigned char BIT4:1;
      unsigned char BIT3:1;
      unsigned char BIT2:1;
      unsigned char BIT1:1;
      unsigned char BIT0:1;
    }
    BIT;
  }
  ICR0;
};

void test_1 (volatile USRSTR* x)
{
  x->ICR0.BIT.BIT5 |= 1;
}

compiled with -O2 -m4 -ml results in:
        mov.b   @r4,r1
        mov.b   @r4,r0
        or      #4,r0
        mov.b   r0,@r4
        rts
        nop

The double load looks wrong.  With normal memory it might do no harm, but when
accessing hardware registers this might result in wrong behavior.  For
instance, sometimes hardware register reads clear status bits etc.

It seems this happens only when the bitfield is read and written back.  If it's
only read, there is only one load:

int test_2 (volatile USRSTR* x)
{
  return x->ICR0.BIT.BIT5;
}

        mov.b   @r4,r0
        tst     #4,r0
        mov     #-1,r0
        rts
        negc    r0,r0

And it happens only when there are bitfields involved:

void test_3 (volatile unsigned char* x)
{
  x[0] |= 4;
}

        mov.b   @r4,r0
        extu.b  r0,r0
        or      #4,r0
        mov.b   r0,@r4
        rts     
        nop

The problem is present on trunk and GCC 5.

Reply via email to