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

            Bug ID: 89251
           Summary: [2018-q4m] Wrong datatype optimization on bitfield
           Product: gcc
           Version: 8.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: david.koch at libertysurf dot fr
  Target Milestone: ---

(probably related to #83784)

The setting :
STM32L476 / Cortex-M4
GCC 8.2.1 from ARM
(https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads)

1- Created several union structures to describe cpu/mcu registers
2- Base type of each is uint32_t
3- Filling in the bitfields of used bits
4- Stuffing with unused bits to keep bits at the right place
5- Access one register, bit 0 to clear the bit
6- Immediate crash with HardFault

typedef union MyRegDef
{ struct
        { uint32_t      enable:1;
        }
        uint32_t        raw;
};

((*MyRegDef) ADDR)->enable = 0;

[BUG] It generates :

LDRH    R0, [R3, #0] <- CRASH
BFC             R0, #0, #1
STRH    R0, [R3, #0]

You cannot access a hardware register on a 2 bytes boundary.

If I change the structure to :

{ struct
        { uint32_t      enable:1;
          uint32_t      _1:30;
        }

[OK] It generates :

LDR             R0, [R3, #0] <- NO PROBLEM
BFC             R0, #0, #1
STR             R0, [R3, #0]

Possible explanation :

The compiler detected that only 1 bit was used and "wanted" to save byte
accesses, hence reducing the size of the load/store to a halfword. It probably
works in memory (provided you have the right alignment), but it doesn't work on
hardware registers.

Possible correction :

rely on the base datatype. If I REALLY wanted it to be on 16 bits, I would have
used uint16_t instead. Don't make some assumptions or at least provide a flag
to allow this "optimization", but don't use it by default.

Reply via email to