Сборка ARM: регистр автоинкремента в магазине - PullRequest
6 голосов
/ 01 февраля 2012

Можно ли автоматически увеличить базовый адрес регистра в STR с помощью [Rn]!?Я изучил документацию, но не смог найти однозначного ответа, главным образом потому, что синтаксис команды представлен как для LDR, так и для STR - теоретически он должен работать для обоих, но я не смог найти никаких примеров autoИнкремент в магазине (загрузка работает нормально).

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

#include <stdio.h>

int main()
{
        int out[]={0, 0};
        asm volatile (
        "mov    r0, #1          \n\t"
        "str    r0, [%0]!       \n\t"
        "add    r0, r0, #1      \n\t"
        "str    r0, [%0]        \n\t"
        :: "r"(out)
        : "r0" );
        printf("%d %d\n", out[0], out[1]);
        return 0;
}

РЕДАКТИРОВАТЬ: пока ответбыл подходящим для регулярных загрузок и хранилищ, я обнаружил, что оптимизатор портит автоинкремент на векторных инструкциях, таких как vldm / vstm.Например, следующая программа

#include <stdio.h>

int main()
{
        volatile int *in = new int[16];
        volatile int *out = new int[16];

        for (int i=0;i<16;i++) in[i] = i;

        asm volatile (
        "vldm   %0!, {d0-d3}            \n\t"
        "vldm   %0,  {d4-d7}            \n\t"
        "vstm   %1!, {d0-d3}            \n\t"
        "vstm   %1,  {d4-d7}            \n\t"
        :: "r"(in), "r"(out)
        : "memory" );

        for (int i=0;i<16;i++) printf("%d\n", out[i]);
        return 0;
}

, скомпилированная с

g++ -O2 -march=armv7-a -mfpu=neon main.cpp -o main

, выдаст бред на выходе последних 8 переменных, поскольку оптимизатор сохраняет инкрементную переменную и использует ее дляпринтф.Другими словами, out[i] на самом деле out[i+8], поэтому первые 8 напечатанных значений - это последние 8 из вектора, а остальные - области памяти вне границ.

Я пробовал с различными комбинациямиключевое слово volatile во всем коде, но поведение изменяется только в том случае, если я компилирую с флагом -O0 или если я использую изменчивый вектор вместо указателя и нового, например

volatile int out[16];

Ответы [ 3 ]

5 голосов
/ 02 февраля 2012

Для хранения и загрузки вы делаете это:

ldr r0,[r1],#4
str r0,[r2],#4

все, что вы ставите в конце, в данном случае 4, добавляется в базовый регистр (r1 в примере ldr и r2 в примере str) после того, как регистр используется для адреса, но до того, как инструкция завершена, она очень похоже на

unsigned int a,*b,*c;
...
a = *b++;
*c++ = a;

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

arm-none-linux-gnueabi-gcc (Sourcery CodeBench Lite 2011.09-70) 4.6.1

#include <stdio.h>
int main ()
{
        int out[]={0, 0};
        asm volatile (
        "mov    r0, #1          \n\t"
        "str    r0, [%0], #4       \n\t"
        "add    r0, r0, #1      \n\t"
        "str    r0, [%0]        \n\t"
        :: "r"(out)
        : "r0" );
        printf("%d %d\n", out[0], out[1]);
        return 0;
}


arm-none-linux-gnueabi-gcc str.c -O2  -o str.elf

arm-none-linux-gnueabi-objdump -D str.elf > str.list


00008380 <main>:
    8380:   e92d4010    push    {r4, lr}
    8384:   e3a04000    mov r4, #0
    8388:   e24dd008    sub sp, sp, #8
    838c:   e58d4000    str r4, [sp]
    8390:   e58d4004    str r4, [sp, #4]
    8394:   e1a0300d    mov r3, sp
    8398:   e3a00001    mov r0, #1
    839c:   e4830004    str r0, [r3], #4
    83a0:   e2800001    add r0, r0, #1
    83a4:   e5830000    str r0, [r3]
    83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
    83ac:   e1a01004    mov r1, r4
    83b0:   e1a02004    mov r2, r4
    83b4:   ebffffe5    bl  8350 <_init+0x20>
    83b8:   e1a00004    mov r0, r4
    83bc:   e28dd008    add sp, sp, #8
    83c0:   e8bd8010    pop {r4, pc}
    83c4:   0000854c    andeq   r8, r0, ip, asr #10

так что

sub sp, sp, #8

- выделить два локальных типа out [0] и out [1]

mov r4,#0
str r4,[sp]
str r4,[sp,#4]

потому что они инициализируются нулем, затем идет встроенная сборка

8398:   e3a00001    mov r0, #1
839c:   e4830004    str r0, [r3], #4
83a0:   e2800001    add r0, r0, #1
83a4:   e5830000    str r0, [r3]

и затем printf:

83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
83ac:   e1a01004    mov r1, r4
83b0:   e1a02004    mov r2, r4
83b4:   ebffffe5    bl  8350 <_init+0x20>

и теперь понятно, почему это не сработало. Вы не объявлены как изменчивые. Вы не дали коду никакой причины вернуться к ram, чтобы получить значения out [0] и out [1] для printf, компилятор знает, что r4 содержит значение как для out [0], так и для out [1]. в этой функции так мало кода, что ему не нужно было выселять r4 и использовать его повторно, поэтому он использовал r4 для printf.

Если вы измените его на изменчивый

    volatile int out[]={0, 0};

Тогда вы должны получить желаемый результат:

83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
83ac:   e59d1000    ldr r1, [sp]
83b0:   e59d2004    ldr r2, [sp, #4]
83b4:   ebffffe5    bl  8350 <_init+0x20>

подготовка к printf читает из оперативной памяти.

0 голосов
/ 02 января 2017

Я нашел этот вопрос, когда искал ответ на похожий вопрос: Как связать регистр ввода / вывода.В документации GCC констант встроенного ассемблера говорится, что префикс + в списке регистров ввода обозначает регистр ввода / вывода.

В этом примере мне кажется, что вы бы предпочли сохранить исходное значениепеременной out.Тем не менее, если вы хотите использовать постинкрементный (!) вариант инструкций, я думаю, что вы должны объявить параметры как чтение / запись.На моем Raspberry Pi 2 сработало следующее:

#include <stdio.h>

int main()
{
  int* in = new int(16);
  volatile int* out = new int(16);

  for (int i=0; i<16; i++) in[i]=i;

  asm volatile(
    "vldm %0!, {d0-d3}\n\t"
    "vldm %0, {d4-d7}\n\t"
    "vstm %1!, {d0-d3}\n\t"
    "vstm %1, {d4-d7}\n\t"
    :"+r"(in), "+r"(out) :: "memory");

  for (int i=0; i<16; i++) printf("%d\n", out[i-8]);
  return 0;
}

Таким образом, семантика кода понятна компилятору: будут изменены как указатели in, так и out (увеличено на 8элементы).

Отказ от ответственности: я не знаю, позволяет ли ARM ABI функции свободно перекрывать регистры NEON с d0 по d7.В этом простом примере это, вероятно, не имеет значения.

0 голосов
/ 26 июля 2014

Встроенный ассемблер GCC требует, чтобы все измененные регистры и энергонезависимые переменные были перечислены как выходные или сгустки. Во втором примере GCC может и предполагает, что регистры, выделенные для in и out, не изменяются.

Правильный подход будет:

out_temp = out;
asm volatile ("..." : "+r"(in), "+r"(out_temp) :: "memory" );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...