optimizationarmclang

ARMClang Optimising 8-bit Unaligned Access to 32-bit LDR


I've been attempting to get FatFS to work on a Cortex A9 processor using Arm Compiler 6 (armclang) in Arm Development Studio.

There is an odd behaviour in the compilers optimisation that I wonder whether somebody can explain why it is making it incorrectly.

FatFS has the following helper function, presumably as part of coping with big-endian processors as well as little-endian.

DWORD ld_dword (unsigned char* ptr)    /* Load a 4-byte little-endian word */
{
    DWORD rv;
    rv = ptr[3];
    rv = rv << 8 | ptr[2];
    rv = rv << 8 | ptr[1];
    rv = rv << 8 | ptr[0];
    return rv;
}

Because this is a little-endian mode processor, the compiler is seeing this as an excuse to compile to just:

LDR      r0,[r11,#8]

The problem is however that *ptr is not aligned to a 32-bit boundary - it is specifically a unsigned char* and used as such. When say ptr = 0xFFE2B446, which is a not aligned, the processor is triggering a jump to the Data Abort hander and restarting the image.

Is there something I'm missing here?


Ok, so the fix appears to be adding -mno-unaligned-access compiler flag to prevent this optimisation.

The question then becomes one of if this unaligned access is causing the processor to crash, what is the use of allowing it in the first place?

If it makes a difference, the memory is on-chip RAM on an Arria 10 HPS. Perhaps it's a limitation of this RAM implementation.


Solution

  • The Cortex-A9 supports unaligned access by default (https://developer.arm.com/documentation/ddi0388/b/unaligned-and-mixed-endian-data-access-support/unaligned-data-access-support?lang=en), but it can be disabled by setting a control register bit. Your compiler is probably assuming that unaligned accesses are allowed because of the CPU target. Adding -mno-unaligned-access is exactly what you need to make the compiler understand that unaligned accesses are disallowed on your target.