Понимание поведения gcc относительно AAPCS (на STM32) - PullRequest
2 голосов
/ 26 июня 2019

РЕДАКТИРОВАТЬ: я полностью осознаю, что функция asmCopy может быть не функциональной, мой вопрос больше о поведении gcc при передаче параметров в регистрах.

Я работаю над STM32H7 с использованием STM32CubeIDE, чей конструкторarm-none-eabi-gcc

Уровень оптимизации равен -Os

Я вижу следующее поведение, которое не могу объяснить.Я сделал снимок экрана, чтобы получить параллельно asm и C-код.

Мой C-код вызывает 3 функции.Первый и третий имеют абсолютно одинаковые параметры.

Второй параметр не принимает.вот его код:

static void Reset_Cycle_Counter(void)
{
    volatile unsigned long *DWT_CYCCNT = (unsigned long *)0xE0001004;
    volatile unsigned long *DWT_CONTROL = (uint32_t *)0xE0001000;

    // Reset cycle counter
    *DWT_CONTROL = *DWT_CONTROL & ~0x00000001 ;
    *DWT_CYCCNT = 0;
    *DWT_CONTROL = *DWT_CONTROL | 1 ;
}

Третья функция особенная: я пытаюсь написать некоторый ассемблерный код (который вполне может быть неправильным прямо сейчас).

static void __attribute__((noinline)) asmCopy(void *dst, void *src, uint32_t bytes)
{
    while (bytes--)
    {
        asm("ldrb r12,[r1], #1"); // src param is stored in r1, r12 can be modified without being restored after
        asm("strb r12,[r0], #1"); // dst paramis stored in r0
    }
}

ПередПри первом вызове функции (в memcpy) r0, r1 и r2 загружаются с правильными значениями.

enter image description here

Затем перед вызовом третьей функции, какниже вы можете видеть, что параметры в r1 и r2 неверны (qspi_addr должен быть 0x90000000).enter image description here

Я понимаю, что AAPCS (стандарт вызова процедур в ARM) заключается в том, что перед вызовом подпрограммы регистры с r0 по r3 должны быть загружены с параметрами функций (если таковые имеются).).И подпрограмма не должна сохранять или восстанавливать эти регистры.Тогда нормально, что вторая функция изменяет r1 и r2.Поэтому я ожидаю, что компилятор обновит r0, r1 и r2 перед третьим вызовом.

Если я изменю код оптимизации на -O0, я действительно получу это ожидаемое поведение.

Что выдумаете?

Ответы [ 3 ]

3 голосов
/ 26 июня 2019

Нельзя просто открыть встроенный блок сборки и предположить, что r0 и r1 по-прежнему содержат аргументы функции.Там нет никакой гарантии для этого вообще.Если вам нужно использовать аргументы, вам нужно правильно передать их в качестве входных и / или выходных операндов

static void __attribute__((noinline))
myAsmCopy(void* dst, void* src, uint32_t bytes) {
  asm volatile("1: cbz %[bytes], 1f \n"
               "ldrb r12, [%[src]], #1 \n"
               "strb r12, [%[dst]], #1 \n"
               "subs %[bytes], #1 \n"
               "b 1b \n"
               "1: \n"
               : [dst] "+&r"(dst), [src] "+&r"(src), [bytes] "+&r"(bytes)
               :
               : "cc", "memory", "r12");
}

GCC имеет здесь обширную документацию по встроенной сборке: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

As you 'Я, очевидно, никогда не использовал ничего из этого, прежде чем я настоятельно советую против этого.Если «С содержит в себе ружья», то при сборке в линию на голову наносится револьвер из 6 выстрелов с 5 пулями.

0 голосов
/ 27 июня 2019

Я думаю, что нашел ответ.

В тестируемой функции (будь то дрянная, которую я реализовал, или лучше из @Vinci) некоторые параметры, передаваемые в функцию, являются глобальными переменными (массивами фиктивных данных для запуска некоторых тестов).

Насколько я понимаю, компилятор "модифицирует" прототип функции, чтобы построить функцию, принимающую только один параметр. Остальные параметры считаются константами и просто ПК относительно загружены в начале функции.

Итак, я изменил код для вызова той же функции, но с локальными изменчивыми указателями, и проблема исчезла: я вижу регистры r0, r1 и r2, загруженные с параметрами, как я и ожидал.

Имеет ли это смысл?

0 голосов
/ 26 июня 2019

Если попытаться спросить у компилятора, как его архивировать, все становится намного проще

https://godbolt.org/z/rXxeRe

void __attribute__((noinline)) asmCopy(void *dst, void *src, uint32_t bytes)
{
    while (bytes--)
    {
        asm("ldrb r12,[r1], #1"); // src param is stored in r1, r12 can be modified without being restored after
        asm("strb r12,[r0], #1"); // dst paramis stored in r0
    }
}

void __attribute__((noinline)) asmCopy1(void *dst, void *src, uint32_t bytes)
{
    while (bytes--)
    {
        *(uint8_t *)dst++ = *(uint8_t *)src++;
    }
}

и код

asmCopy:
.L2:
        adds    r2, r2, #-1
        bcs     .L3
        bx      lr
.L3:
        ldrb r12,[r1], #1
        strb r12,[r0], #1
        b       .L2
asmCopy1:
        subs    r0, r0, #1
        add     r2, r2, r1
.L5:
        cmp     r1, r2
        bne     .L6
        bx      lr
.L6:
        ldrb    r3, [r1], #1    @ zero_extendqisi2
        strb    r3, [r0, #1]!
        b       .L5
...