ARM, VFP, плавающая точка, ленивое переключение контекста - PullRequest
2 голосов
/ 26 мая 2020

Пишу операционную систему для процессора ARM (Cortex-A9).

Я пытаюсь реализовать ленивое переключение контекста регистров с плавающей запятой. Идея заключается в том, что расширение с плавающей запятой изначально отключено для потока, и поэтому нет необходимости сохранять контекст с плавающей запятой при переключении задач.

Когда поток пытается использовать точечная инструкция, она вызывает исключение. Затем операционная система включает расширение с плавающей запятой и знает, что контекст с плавающей запятой должен быть сохранен для этого потока при следующих переключениях контекста. Затем повторно выполняется инструкция с плавающей запятой.

Моя проблема в том, что компилятор генерирует инструкции с плавающей запятой, даже если в коде c не используются операции с плавающей запятой. Это пример дизассемблирования функции, которая не использует плавающую точку в c:

10002f5c <rmtcpy_from>:
10002f5c:   e1a0c00d    mov ip, sp
10002f60:   e92ddff0    push    {r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}
10002f64:   e24cb004    sub fp, ip, #4
10002f68:   ed2d8b02    vpush   {d8}
...
10002f80:   ee082a10    vmov    s16, r2
...
10002fe0:   ee180a10    vmov    r0, s16
...
1000308c:   ecbc8b02    vldmia  ip!, {d8}
...

Когда у меня много таких функций, ленивое переключение контекста не имеет смысла.

Кто-нибудь знает, как сообщить компилятору, что инструкции с плавающей запятой должны создаваться только тогда, когда в коде c есть операция с плавающей запятой?

Я использую g cc 9.2.0. Параметры с плавающей запятой: -mhard-float -mfloat-abi=hard -mfpu=vfp

Вот пример c функции (не используется, только демонстрация):

void func(char *a1, char *a2, char *a3);
int bar_1[1], foo_1, foo_2;

void fpu_test() {
    int oldest_idx = -1;
    while (1) {
        int *oldest = (int *)0;
        int idx = oldest_idx;
        for (int i = 0; i < 3; i++) {
            if (++idx >= 3)
                idx = 0;
            int *lec = &bar_1[idx];
            if (*lec) {
                if (*lec - *oldest < 0) {
                    oldest = lec;
                    oldest_idx = idx;
                }
            }
        }
        if (oldest) {
            foo_1++;
            if (foo_2)
                func("1", "2", "3");
        }
    }
}

g cc командная строка:

$HOME/devel/opt/cross-musl/bin/arm-linux-musleabihf-gcc  -O2 -march=armv7-a -mtune=cortex-a9 -mhard-float -mfloat-abi=hard -mfpu=vfp -Wa,-ahlms=fpu_test.lst -mapcs-frame -c fpu_test.c -o fpu_test.o

Список ассемблера:

...
  35 0000 0DC0A0E1      mov ip, sp
  36 0004 003000E3      movw    r3, #:lower16:foo_2
  37 0008 F0DF2DE9      push    {r4, r5, r6, r7, r8, r9, r10, fp, ip, lr, pc}
  38 000c 006000E3      movw    r6, #:lower16:foo_1
  39 0010 003040E3      movt    r3, #:upper16:foo_2
  40 0014 04B04CE2      sub fp, ip, #4
  41 0018 006040E3      movt    r6, #:upper16:foo_1
  42 001c 004000E3      movw    r4, #:lower16:bar_1
  43 0020 028B2DED      vpush.64    {d8}                <=== this is the problem
...

Ответы [ 2 ]

1 голос
/ 27 мая 2020

G CC имеет для этого переключатель командной строки -mgeneral-regs-only. . При использовании переключателя командной строки вам может потребоваться разделить код, который намеренно использует регистры с плавающей запятой или операции, в отдельные исходные файлы, чтобы его можно было скомпилировать без этого переключателя.

Начиная с G CC 9,3 (возможно, 9?), Для целей ARM это доступно как атрибут функции:

void MyFunction(char *MyParameter) __attribute__ ((general-regs-only));

Размещение атрибута после объявления является более старым синтаксисом и требует объявления без определения . Тестирование предполагает, что G CC теперь принимает атрибут перед декларатором и может использоваться с определением:

void __attribute__ ((general-regs-only)) MyFunction(char *MyParameter)
{...}

Вы также можете отрицать атрибут с помощью __attribute__ ((nogeneral-regs-only)), хотя я этого не вижу задокументировано.

Этим также можно управлять с помощью прагмы .

В переключателях -march и -mcpu также есть параметры +nofp, но я думаю -mgeneral-regs-only - это то, что вы хотите.

0 голосов
/ 14 июля 2020

Просто обратите внимание на атрибут функции: __attribute__ ((general-regs-only)) кажется, не работает на g cc 9.3.1.

Попробуйте использовать атрибут 'target'. Например:

void __attribute__((target("general-regs-only"))) MyFunction(char *MyParameter)
{...}

Кажется, что отрицание "только для общих регистров" не работает в g ​​cc 9.3.1.

...