Как указать отдельный регистр в качестве ограничения во встроенной сборке ARM GCC?

в встроенной сборке x86 я могу написать что-то вроде этого:

asm ("cpuid"
            : "=a" (_eax),
              "=b" (_ebx),
              "=c" (_ecx),
              "=d" (_edx)
            : "a" (op));

поэтому в ограничениях сопоставления вместо того, чтобы просто писать "= r" и позволить компилятору выбрать регистр, я могу сказать, какой конкретный регистр я хочу использовать (= a, например, для использования% eax)

как это сделать для сборки ARM? кулинарная книга сборки ARM GCC http://www.ethernut.de/en/documents/arm-inline-asm.html указано, что я могу, например, использовать ограничение «r» для одного из регистров общего назначения R0-R15 «w» для одного из регистров с плавающей запятой VFP S0-S31.

но как я могу ограничить операнд, например, точно s1? или в конкретный регистр общего назначения?


person Mat    schedule 14.10.2010    source источник
comment
Какую проблему можно решить в ARM, чтобы точно знать, в какой регистр помещен конкретный операнд? Какие инструкции на ARM неявно изменяют регистры (кроме операций загрузки / сохранения до / после инкремента / декремента, для которых существуют определенные ограничения)? В этом аспекте ARM и x86 совершенно разные ... то, что нужно для x86, не для ARM.   -  person FrankH.    schedule 21.05.2013
comment
@FrankH. Системные вызовы? Вот как я нашел этот вопрос.   -  person Duc    schedule 25.06.2016


Ответы (2)


Я не думаю, что gcc для ARM позволяет вам использовать ограничения для указания какой именно регистр использовать. Однако вы можете использовать явные регистровые переменные, чтобы указать регистр для хранения переменной:

register int my_variable asm("r0");
person Mike Seymour    schedule 14.10.2010

Минимальный исполняемый пример явных регистровых переменных

Вот автономный приветственный мир ARMv8 Linux C, иллюстрирующий https://stackoverflow.com/a/3936064/9160762 с некоторой разборкой анализ:

main.c

#include <inttypes.h>

void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
        uint64_t syscall_return;
        register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
        register char *x1 __asm__ ("x1") = msg;
        register uint64_t x2 __asm__ ("x2") = sizeof(msg);
        register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
        __asm__ __volatile__ (
            "svc 0;"
            : "+r" (x0)
            : "r" (x1), "r" (x2), "r" (x8)
            : "memory"
        );
        syscall_return = x0;
        exit_status = (syscall_return != sizeof(msg));
    }

    /* exit */
    {
        register uint64_t x0 __asm__ ("x0") = exit_status;
        register uint64_t x8 __asm__ ("x8") = 93;
        __asm__ __volatile__ (
            "svc 0;"
            : "+r" (x0)
            : "r" (x8)
            :
        );
    }
}

вверх по течению на GitHub.

Скомпилируйте и запустите:

sudo apt-get install qemu-user gcc-aarch64-linux-gnu
aarch64-linux-gnu-gcc -O3 -std=c99 -ggdb3 -march=armv8-a -pedantic -Wall -Wextra \
  -ffreestanding -nostdlib -static -o main.out main.c
qemu-aarch64 main.out

Вывод:

hello syscall v8

Разборка:

aarch64-linux-gnu-objdump -S main.out

Вывод:

main.out:     file format elf64-littleaarch64


Disassembly of section .text:

0000000000400110 <_start>:
void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
  400110:   90000003    adrp    x3, 400000 <_start-0x110>
  400114:   91056063    add x3, x3, #0x158
void _start(void) {
  400118:   d10083ff    sub sp, sp, #0x20
        uint64_t syscall_return;
        register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
  40011c:   d2800020    mov x0, #0x1                    // #1
        register char *x1 __asm__ ("x1") = msg;
  400120:   910023e1    add x1, sp, #0x8
        register uint64_t x2 __asm__ ("x2") = sizeof(msg);
  400124:   d2800242    mov x2, #0x12                   // #18
        char msg[] = "hello syscall v8\n";
  400128:   a9401464    ldp x4, x5, [x3]
        register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
  40012c:   d2800808    mov x8, #0x40                   // #64
        char msg[] = "hello syscall v8\n";
  400130:   79402063    ldrh    w3, [x3, #16]
  400134:   a90097e4    stp x4, x5, [sp, #8]
  400138:   790033e3    strh    w3, [sp, #24]
        __asm__ __volatile__ (
  40013c:   d4000001    svc #0x0
            : "+r" (x0)
            : "r" (x1), "r" (x2), "r" (x8)
            : "memory"
        );
        syscall_return = x0;
        exit_status = (syscall_return != sizeof(msg));
  400140:   eb02001f    cmp x0, x2
    }

    /* exit */
    {
        register uint64_t x0 __asm__ ("x0") = exit_status;
        register uint64_t x8 __asm__ ("x8") = 93;
  400144:   d2800ba8    mov x8, #0x5d                   // #93
        register uint64_t x0 __asm__ ("x0") = exit_status;
  400148:   9a9f07e0    cset    x0, ne  // ne = any
        __asm__ __volatile__ (
  40014c:   d4000001    svc #0x0
            : "+r" (x0)
            : "r" (x8)
            :
        );
    }
}
  400150:   910083ff    add sp, sp, #0x20
  400154:   d65f03c0    ret

Попытка без явных регистровых переменных

В основном ради удовольствия я пытался достичь того же результата без использования регистровых переменных, но мне это не удалось.

В любом случае код будет более сложным, поэтому лучше просто использовать регистровые переменные.

Вот моя лучшая попытка:

main.c

#include <inttypes.h>

void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
        uint64_t syscall_return;
        __asm__ (
            "mov x0, 1;" /* stdout */
            "mov x1, %[msg];"
            "mov x2, %[len];"
            "mov x8, 64;" /* syscall number */
            "svc 0;"
            "mov %[syscall_return], x0;"
            : [syscall_return] "=r" (syscall_return)
            : [msg] "p" (msg),
            [len] "i" (sizeof(msg))
            : "x0", "x1", "x2", "x8", "memory"
        );
        exit_status = (syscall_return != sizeof(msg));
    }

    /* exit */
    __asm__ (
        "mov x0, %[exit_status];"
        "mov x8, 93;" /* syscall number */
        "svc 0;"
        :
        : [exit_status] "r" (exit_status)
        : "x0", "x8"
    );
}

GitHub upstream.

Разборка:

main.out:     file format elf64-littleaarch64


Disassembly of section .text:

0000000000400110 <_start>:
void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
  400110:   90000000        adrp    x0, 400000 <_start-0x110>
  400114:   9105a000        add     x0, x0, #0x168
void _start(void) {
  400118:   d10083ff        sub     sp, sp, #0x20
        char msg[] = "hello syscall v8\n";
  40011c:   a9400c02        ldp     x2, x3, [x0]
  400120:   a9008fe2        stp     x2, x3, [sp, #8]
  400124:   79402000        ldrh    w0, [x0, #16]
        uint64_t syscall_return;
        __asm__ (
  400128:   910023e3        add     x3, sp, #0x8
        char msg[] = "hello syscall v8\n";
  40012c:   790033e0        strh    w0, [sp, #24]
        __asm__ (
  400130:   d2800020        mov     x0, #0x1                        // #1
  400134:   aa0303e1        mov     x1, x3
  400138:   d2800242        mov     x2, #0x12                       // #18
  40013c:   d2800808        mov     x8, #0x40                       // #64
  400140:   d4000001        svc     #0x0
  400144:   aa0003e3        mov     x3, x0
            : [syscall_return] "=r" (syscall_return)
            : [msg] "p" (msg),
            [len] "i" (sizeof(msg))
            : "x0", "x1", "x2", "x8", "memory"
        );
        exit_status = (syscall_return != sizeof(msg));
  400148:   f100487f        cmp     x3, #0x12
  40014c:   9a9f07e1        cset    x1, ne  // ne = any
    }

    /* exit */
    __asm__ (
  400150:   aa0103e0        mov     x0, x1
  400154:   d2800ba8        mov     x8, #0x5d                       // #93
  400158:   d4000001        svc     #0x0
        "svc 0;"
        :
        : [exit_status] "r" (exit_status)
        : "x0", "x8"
    );
}
  40015c:   910083ff        add     sp, sp, #0x20
  400160:   d65f03c0        ret

Это было менее эффективно по следующим причинам:

  • write ограничение p необходимо использовать промежуточный регистр x3 для add - sp

  • Я не знаю, как получить статус возврата системного вызова без дополнительного mov в выходной регистр

  • exit статус перемещается еще раз через x1. С регистровыми переменными просто вычисляется прямо в x0.

Протестировано в Ubuntu 18.10, GCC 8.2.0, QEMU 2.12.

person Ciro Santilli    schedule 23.02.2019
comment
Это небезопасно: ничто не сообщает компилятору, что память, на которую указывает `r (msg)`, также является входом в asm. При оптимизации gcc оптимизирует явно мертвое хранилище в массив стека. Вам нужен "memory" clobber или фиктивный ввод, например "m"( *(char (*)[]) msg ) (разыменованный указатель на массив: т.е. весь массив является операндом памяти). Или, поскольку мы знаем длину, "m"( *(char (*)[len]) msg ). - person Peter Cordes; 24.02.2019
comment
Вам не нужно тратить инструкции на mov внутри asm-оператора. Принятый ответ должен работать: register const char * foo asm("x1") = "blah blah"; will make r (foo) `pick x1. IDK, почему вы приводите этот большой пример дерьмового неоптимального способа. - person Peter Cordes; 24.02.2019
comment
@PeterCordes а, я не думал об этом, что это будет менее эффективно. Я пытался избежать регистровых переменных, поскольку они уродливы, но я не вижу решения в этом случае. Я обновлю это. - person Ciro Santilli; 24.02.2019
comment
Это именно то, для чего нужны register ... asm местные жители. Фактически, это единственный поддерживаемый вариант использования. Некоторые другие вещи (например, использование их для чтения регистра без оператора asm) также обычно работают, но несколько недавнее изменение документации делает это неподдерживаемым. Вы можете заключить объявления переменных + asm в область видимости { } в более крупную функцию или, чаще всего, просто в тело встроенной функции-оболочки. - person Peter Cordes; 24.02.2019
comment
@PeterCordes, давайте попробуем еще раз :-) - person Ciro Santilli; 24.02.2019