Data dependency detection bug with arm-none-eabi-gcc and -Ofast

Viewed 41

I am writing a program for an Arm Cortex M7 based microcontroller, where I am facing an issue with (I guess) compiler optimization. I compile using arm-none-eabi-gcc v10.2.1 with -mcpu=cortex-m7 -std=gnu11 -Ofast -mthumb.

I extracted the relevant parts, they are as below. I copy some data from a global variable into a local one, and from there the data is written to a hardware CRC engine to compute the CRC over the data. Before writing it to the engine, the byte order is to be reversed.

#include <stddef.h>
#include <stdint.h>
#define MEMORY_BARRIER asm volatile("" : : : "memory")
#define __REV(x) __builtin_bswap32(x) // Reverse byte order

// Dummy struct, similiar as it is used in our program
typedef struct {
    float n1, n2, n3, dn1, dn2, dn3;
} test_struct_t;

// CRC Engine Registers
typedef struct {
    volatile uint32_t DR; // CRC Data register Address offset: 0x00
} CRC_TypeDef;
#define CRC ((CRC_TypeDef*) (0x40000000UL))

// Write data to CRC engine
static inline void CRC_write(uint32_t* data, size_t size)
{
    for(int index = 0; index < (size / 4); index++) {
        CRC->DR = __REV(*(data + index));
    }
}

// Global variable holding some data, similiar as it is used in our program
volatile test_struct_t global_var = { 0 };

int main()
{
    test_struct_t local_tmp;
    MEMORY_BARRIER;
    // Copy the current data into a local variable
    local_tmp = global_var;
    // Now, write the data to the CRC engine to compute CRC over the data
    CRC_write((uint32_t*) &local_tmp, sizeof(local_tmp));
    MEMORY_BARRIER;
    return 0;
}

This compiles to:

main:
        push    {r4, r5, lr}
        sub     sp, sp, #28
        ldr     r4, .L4
        mov     ip, sp
        ldr     r3, [sp]              ; load first float into r3
        mov     lr, #1073741824       ; load address of CRC engine DR register into lr
        rev     r5, r3                ; reverse byte order of first float and store in r5
        ldmia   r4!, {r0, r1, r2, r3} ; load first 4 floats into r0-r3
        stmia   ip!, {r0, r1, r2, r3} ; copy first 4 floats into local variable
        ldm     r4, {r0, r1}          ; load float 5 and 6 into r0 and r1
        stm     ip, {r0, r1}          ; copy float 5 and 6 into local variable
        str     r5, [lr]              ; write reversed bytes from r5 to CRC engine
        ldr     r3, [sp, #4]
        rev     r3, r3
        str     r3, [lr]
        ldr     r3, [sp, #8]
        rev     r3, r3
        str     r3, [lr]
        ldr     r3, [sp, #12]
        rev     r3, r3
        str     r3, [lr]
        ldr     r3, [sp, #16]
        rev     r3, r3
        str     r3, [lr]
        ldr     r3, [sp, #20]
        rev     r3, r3
        str     r3, [lr]
        movs    r0, #0
        add     sp, sp, #28
        pop     {r4, r5, pc}
.L4:
        .word   .LANCHOR0
global_var:

The problem is now that the first float is loaded and its byte order is reversed (rev r5, r3) before the data has been copied from the global variable (happens with the ldmia/sdmia). This is clearly not intended, leads to a wrong CRC, and I wonder why the compiler does not recognize this data dependency. Am I doing anything wrong or is there a bug in GCC?

Code in compiler explorer: https://godbolt.org/z/ErWThc5dx

0 Answers
Related