Перейти к содержанию
    

Как корректно использовать LDM/STM в inline asm IAR? Кто-нибудь может привести пример?

Есть такой код (вариант_1):

    u32 *p0 = ..., *p1 = ...;
    uint i = ...;
    u32 j0, j1, j2, j3;
    asm(
      "p01: LDR   %0, [%4, #8 * 1]   \n"
      "     LDR   %1, [%4, #8 * 2]   \n"
      "     LDR   %2, [%4, #8 * 3]   \n"
      "     LDR   %3, [%4, #8 * 4]!  \n"
      "     SUBS  %6, %6, #4         \n"
      "     STMIA %5!, {%0,%1,%2,%3} \n"
      "     BNE      p01"
      : "=&r"(j0), "=&r"(j1), "=&r"(j2), "=&r"(j3)
      : "r"(p0), "r"(p1), "r"(i)
      : "cc", "memory");
    j0 = j0; j1 = j1; j2 = j2; j3 = j3;

После его компиляции ожидаемо получаем ерунду:

0x6896             LDR   R6, [R2, #8 * 1]  
0x6915             LDR   R5, [R2, #8 * 2]  
0x6994             LDR   R4, [R2, #8 * 3]  
0xF852 0x3F20      LDR   R3, [R2, #8 * 4]! 
0xF1BC 0x0C04      SUBS  R12, R12, #4      
0xC778             STMIA R7!, {R6,R5,R4,R3}
0xD1F6             BNE      ??p01

Неверный порядок чтения в R3...R6.

Но как компилятору указать, что нужно читать в порядке: R3,R4,R5,R6? Не вижу средств для этого в inline asm.

Можно конечно использовать регистровую пару (вариант_2):

    u32 *p0 = ..., *p1 = ...;
    uint i = ...;
    u64 q;
    asm(
      "p01: LDR   %L0, [%1, #8 * 1]  \n"
      "     LDR   %H0, [%1, #8 * 2]! \n"
      "     SUBS  %3, %3, #2         \n"
      "     STMIA %2!, {%L0,%H0}     \n"
      "     BNE      p01"
      : "=&Rp"(q)
      : "r"(p0), "r"(p1), "r"(i)
      : "cc", "memory");
    q = q;

тогда на выходе всё ок:

0x6896             LDR   R6, [R2, #8 * 1] 
0xF852 0x7F10      LDR   R7, [R2, #8 * 2]!
0x1E9B             SUBS  R3, R3, #2       
0xC1C0             STMIA R1!, {R6,R7}     
0xD1F9             BNE      ??p01

Но всего 2 регистра. :cray:маловато...

Можно также заPUSHить нужное число регистров перед циклом (с явным указанием нужных регистров) и потом разPOPить. Но это - лишние операции чтения/записи, так как перед этим циклом си-функция использует много регистров и они и так сохранены в стеке, а после данного цикла - выход из функции, так что почти все регистры можно использовать для цикла. :cray:

 

В документации на inline asm IAR нашёл упоминание некоего модификатора (модификатора чего???) - 'M'. Описанного как:

Цитата

M - For a register or a register pair, the register list suitable for ldm or stm. Cannot be transformed by additional operand modifiers.

Чую - это то, что надо. Но нигде нет никакого инструкции - как его применять??? :unknw:

Также в инете нашёл описание этого модификатора: https://releases.llvm.org/12.0.0/docs/LangRef.html

Спойлер
Цитата

ARM:

a: Print an operand as an address (with [ and ] surrounding a register).

P: No effect.

q: No effect.

y: Print a VFP single-precision register as an indexed double (e.g. print as d4[1] instead of s9)

B: Bitwise invert and print an immediate integer constant without # prefix.

L: Print the low 16-bits of an immediate integer constant.

M: Print as a register set suitable for ldm/stm. Also prints all register operands subsequent to the specified one (!), so use carefully.

Q: Print the low-order register of a register-pair, or the low-order register of a two-register operand.

R: Print the high-order register of a register-pair, or the high-order register of a two-register operand.

H: Print the second register of a register-pair. (On a big-endian system, H is equivalent to Q, and on little-endian system, H is equivalent to R.)

e: Print the low doubleword register of a NEON quad register.

f: Print the high doubleword register of a NEON quad register.

m: Print the base register of a memory operand without the [ and ] adornment.

 

Но опять - совершенно ничего непонятно из этого куцего описания! Ни одного примера использования 'M' не могу найти. Пробовал вставлять его в разных местах asm() - не нашёл взаимопонимания с компилятором.

Может кто встречал примеры использования модификатора 'M'? Или подскажет другой способ корректного использования LDM/STM?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что запрещает сделать .s-файл и запускать из Си реализованную там функцию?

Зачем вы мешаете компилятору делать свою работу?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, adnega сказал:

А что запрещает сделать .s-файл и запускать из Си реализованную там функцию?

То же самое, что мешает заPUSHить/разPOPить пачку регистров до/после цикла - нежелание лишних операций чтения/записи.

Передача управления в сторонний asm-файл и сохранение/восстановление регистров там - это ничем не лучше.

4 минуты назад, adnega сказал:

Зачем вы мешаете компилятору делать свою работу?

Чем мешаю? Я вроде как пытаюсь ему помочь как раз. Компилятор IAR не умеет использовать LDM/STM. Вообще про них не знает как будто! :cray: 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А так получится (указать явно имена регистров)?

void vPortStartFirstTask( void )
{
	__asm volatile(
					" ldr r0, =0xE000ED08 	\n" /* Use the NVIC offset register to locate the stack. */
					" ldr r0, [r0] 			\n"
					" ldr r0, [r0] 			\n"
					" msr msp, r0			\n" /* Set the msp back to the start of the stack. */
					" cpsie i				\n" /* Globally enable interrupts. */
					" svc 0					\n" /* System call to start first task. */
					" nop					\n"
				);
}

 

В 12.04.2023 в 13:47, jcxz сказал:

LDM/STM. Вообще про них не знает как будто! :cray: 

Это довольно опасные инструкции. Например, у вас могут быть данные не выровненные - тогда их нельзя использовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, adnega сказал:

А так получится (указать явно имена регистров)?

Явно указывать нельзя. Так как будет конфликт с используемыми си-компилятором. Цикл - у конца функции, но не в самом её конце (писал выше). И одна запись должна быть выполнена после цикла. К тому же - при явном использовании регистров компилятор об этом не знает и может не сохранить их в прологе/эпилоге функции.

4 минуты назад, adnega сказал:

Например, у вас могут быть данные не выровненные

Данные выровнены.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 12.04.2023 в 13:53, jcxz сказал:

Данные выровнены.

А можно попробовать LDRD/STRD ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, adnega сказал:

А можно попробовать LDRD/STRD ?

Чем это лучше варианта_2?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 12.04.2023 в 14:01, jcxz сказал:

Чем это лучше варианта_2?

Вариант_2 вас устроил только на половину. Может, LDRD/STRD добавит вторую половину.

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

Не совсем понятно, чем вариант_1 плох? Почему важен именно порядок регистров?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

31 минуту назад, jcxz сказал:

тому же - при явном использовании регистров компилятор об этом не знает и может не сохранить их в прологе/эпилоге функции.

Надо ему об этом сказать, типа такого:

asm(
      "......"
      : .....
      : .....
      : "cc", "memory", "r0");

Не уверен, что написал правильно, но в gcc точно такая возможность была.

Вот, нашел:

We can read and write the clobbered registers as many times as we like. Consider the example of multiple instructions in a template; it assumes the subroutine _foo accepts arguments in registers eax and ecx.

        asm ("movl %0,%%eax;
              movl %1,%%ecx;
              call _foo"
             : /* no outputs */
             : "g" (from), "g" (to)
             : "eax", "ecx"
             );

 

1 час назад, jcxz сказал:

Но опять - совершенно ничего непонятно из этого куцего описания! Ни одного примера использования 'M' не могу найти. Пробовал вставлять его в разных местах asm() - не нашёл взаимопонимания с компилятором.

эмм... А так пробовали?

  u32 *p0 = ..., *p1 = ...;
    uint i = ...;
    u32 j0, j1, j2, j3;
    asm(
      "p01: LDR   %0, [%4, #8 * 1]   \n"
      "     LDR   %1, [%4, #8 * 2]   \n"
      "     LDR   %2, [%4, #8 * 3]   \n"
      "     LDR   %3, [%4, #8 * 4]!  \n"
      "     SUBS  %6, %6, #4         \n"
      "     STMIA %5!, {%0,%1,%2,%3} \n"
      "     BNE      p01"
      : "=&M"(j0), "=&M"(j1), "=&M"(j2), "=&M"(j3)
      : "r"(p0), "r"(p1), "r"(i)
      : "cc", "memory");
    j0 = j0; j1 = j1; j2 = j2; j3 = j3;

В gcc M означает другое (совпадает с llvm, кстати), так что сам проверить не могу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

36 минут назад, adnega сказал:

Вариант_2 вас устроил только на половину. Может, LDRD/STRD добавит вторую половину.

Вариант_2 - только пару регистров использует. Что равнозначно LDRD/STRD.

Больше регистров одновременно читаемых/записываемых - больше скорость.

36 минут назад, adnega сказал:

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

Я хочу использовать неявно. Но - в явно заданном порядке. Чтобы в командах чтения номера регистров нарастали от первой команды к последней.

36 минут назад, adnega сказал:

Не совсем понятно, чем вариант_1 плох? Почему важен именно порядок регистров?

Вы можете прочитать фразу " меч ирав_тналп 1 ?хо" ?

А ведь это ваша фраза "чем вариант_1 плох? " к которой применён вариант_1 (только вместо слов - байты).

35 минут назад, Сергей Борщ сказал:

Не уверен, что написал правильно, но в gcc точно такая возможность была.

Хмммм.... не подумал. Надо попробовать. Спасибо!

35 минут назад, Сергей Борщ сказал:

эмм... А так пробовали?

Так не будет работать, потому что тут 'M' становится командой, а не модификатором. А в качестве команды у 'M' другая функция:

Цитата

M - An immediate constant that is a multiple of 4 in the range 0 to 1020 (only valid for Thumb1).

Нужен именно модификатор 'M'.

Поэтому пробовал разные варианты типа: : "=&Mr"(j0), "=&Mr"(j1), "=&Mr"(j2), "=&Mr"(j3)

и подобных. Но не работает  :sad:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

40 минут назад, Сергей Борщ сказал:

Не уверен, что написал правильно, но в gcc точно такая возможность была.

Это сработало!

    u32 *p0 = ..., *p1 = ...;
    i = ...;
    asm(
      "p01: LDR   R0, [%0, #8 * 1]   \n"
      "     LDR   R1, [%0, #8 * 2]   \n"
      "     LDR   R2, [%0, #8 * 3]   \n"
      "     LDR   R3, [%0, #8 * 4]!  \n"
      "     SUBS  %2, %2, #4         \n"
      "     STMIA %1!, {R0-R3} \n"
      "     BNE      p01"
      :
      : "r"(p0), "r"(p1), "r"(i)
      : "cc", "memory", "R0", "R1", "R2", "R3");

Выхлоп:

0x68B0             LDR   R0, [R6, #8 * 1] 
0x6931             LDR   R1, [R6, #8 * 2] 
0x69B2             LDR   R2, [R6, #8 * 3] 
0xF856 0x3F20      LDR   R3, [R6, #8 * 4]!
0xF1BC 0x0C04      SUBS  R12, R12, #4     
0xC70F             STMIA R7!, {R0-R3}     
0xD1F6             BNE      ??p01         
0x8025             STRH     R5,[R4, #+0]

Спасибо!

 

ЗЫ: Даже R12 IAR тут задействовал. Чего он обычно старательно избегает...  :wink2:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А так попробовать:

    u32 *p0 = ..., *p1 = ...;
    uint i = ...;
    u32 j0, j1, j2, j3;
    asm(
      "p01: LDR   %0, [%4, #8 * 1]   \n"
      "     LDR   %1, [%4, #8 * 2]   \n"
      "     LDR   %2, [%4, #8 * 3]   \n"
      "     LDR   %3, [%4, #8 * 4]!  \n"
      "     SUBS  %6, %6, #4         \n"
      "     STMIA %5!, {%0,%1,%2,%3} \n"
      "     BNE      p01"
      : "=r"(j0), "=&r"(j1), "=&r"(j2), "=&r"(j3)
      : "r"(p0), "r"(p1), "r"(i)
      : "cc", "memory");
    j0 = j0; j1 = j1; j2 = j2; j3 = j3;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, adnega сказал:

Стоп. Тут же IAR ерунду пишет:

0xC778             STMIA R7!, {R6,R5,R4,R3}

Почему? Всё нормально.

3 минуты назад, adnega сказал:

А так попробовать:

Ну так у меня так и написано. См. первый пост.

Почти так. Только Вы зачем-то удалили '&' из первого операнда.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

51 минуту назад, adnega сказал:

Не совсем понятно, чем вариант_1 плох? Почему важен именно порядок регистров?

...

Стоп. Тут же IAR ерунду пишет:

LDM/STM пофигу, в каком порядке перечислен {reglist}. Там в КОП инструкции эти регистры отмечаются битовой маской. А инструкция проходится по списку этих битов, начиная с младшего.

Поэтому важен порядок лишь загрузки данных в сами регистры, а не то, как они выглядят в списке LDM/STM.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...