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

            Bug ID: 127388
           Summary: armv8-m.main prefers to spill r4 to the stack; rather
                    than use r12
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: david.c.mcgarry at gmail dot com
  Target Milestone: ---

gcc -march=armv8-m.main -mthumb -Os will not use r12 for a simple ldr/orr/str
sequence.

GCC will push/pop r4 preferring thumb instructions; even though it's 2 extra
instructions and 2 bytes longer. Clang does the right thing.

While the trunk version at godbolt.org appears to actually pop r4, the version
I'm using (.pico-sdk/toolchain/13_2_Rel1/bin/arm-none-eabi-gcc) used a 4 byte
"f85d 4b04 ldr.w r4, [sp], #4". Regardless of pop or ldr.w, it shouldn't be
using the stack at all in this example.

https://www.godbolt.org/z/qGeGPqjG9

#include <string.h>
#include <stdint.h>

struct Test
{
    uintptr_t ptr;

    void set_bit()
    {
        ptr |= 1;
    }

    void set_bit_r12()
    {
        register uintptr_t r12 __asm__("r12") = ptr;
        asm volatile("" : "+r"(r12));
        r12 |= 1;
        asm volatile("" : "+r"(r12));
        ptr = r12;
    }
};

__attribute__((used))
void write(char* dst, const char* src, size_t len, Test& test)
{
    test.set_bit();
    memcpy(dst, src, len);
}

__attribute__((used))
void write_r12(char* dst, const char* src, size_t len, Test& test)
{
    test.set_bit_r12();
    memcpy(dst, src, len);
}


This compiles to:

write(char*, char const*, unsigned int, Test&):
        push    {r4}
        ldr     r4, [r3]
        orr     r4, r4, #1
        str     r4, [r3]
        pop     {r4}
        b       memcpy

write_r12(char*, char const*, unsigned int, Test&):
        ldr     ip, [r3]
        orr     ip, ip, #1
        str     ip, [r3]
        b       memcpy

Reply via email to