jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба Как корректно использовать 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 регистра. маловато... Можно также заPUSHить нужное число регистров перед циклом (с явным указанием нужных регистров) и потом разPOPить. Но это - лишние операции чтения/записи, так как перед этим циклом си-функция использует много регистров и они и так сохранены в стеке, а после данного цикла - выход из функции, так что почти все регистры можно использовать для цикла. В документации на 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. Чую - это то, что надо. Но нигде нет никакого инструкции - как его применять??? Также в инете нашёл описание этого модификатора: 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? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба А что запрещает сделать .s-файл и запускать из Си реализованную там функцию? Зачем вы мешаете компилятору делать свою работу? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 4 минуты назад, adnega сказал: А что запрещает сделать .s-файл и запускать из Си реализованную там функцию? То же самое, что мешает заPUSHить/разPOPить пачку регистров до/после цикла - нежелание лишних операций чтения/записи. Передача управления в сторонний asm-файл и сохранение/восстановление регистров там - это ничем не лучше. 4 минуты назад, adnega сказал: Зачем вы мешаете компилятору делать свою работу? Чем мешаю? Я вроде как пытаюсь ему помочь как раз. Компилятор IAR не умеет использовать LDM/STM. Вообще про них не знает как будто! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба А так получится (указать явно имена регистров)? 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. Вообще про них не знает как будто! Это довольно опасные инструкции. Например, у вас могут быть данные не выровненные - тогда их нельзя использовать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 1 минуту назад, adnega сказал: А так получится (указать явно имена регистров)? Явно указывать нельзя. Так как будет конфликт с используемыми си-компилятором. Цикл - у конца функции, но не в самом её конце (писал выше). И одна запись должна быть выполнена после цикла. К тому же - при явном использовании регистров компилятор об этом не знает и может не сохранить их в прологе/эпилоге функции. 4 минуты назад, adnega сказал: Например, у вас могут быть данные не выровненные Данные выровнены. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба В 12.04.2023 в 13:53, jcxz сказал: Данные выровнены. А можно попробовать LDRD/STRD ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 3 минуты назад, adnega сказал: А можно попробовать LDRD/STRD ? Чем это лучше варианта_2? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба В 12.04.2023 в 14:01, jcxz сказал: Чем это лучше варианта_2? Вариант_2 вас устроил только на половину. Может, LDRD/STRD добавит вторую половину. Я так понимаю проблему: вы хотите явно использовать имена регистров, но при этом хотите, чтоб компилятор о их использовании знал, и какие-то дополнительные вещи делал на автомате? Не совсем понятно, чем вариант_1 плох? Почему важен именно порядок регистров? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 143 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 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, кстати), так что сам проверить не могу. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 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) и подобных. Но не работает Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба Стоп. Тут же IAR ерунду пишет: 0xC778 STMIA R7!, {R6,R5,R4,R3} Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 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 тут задействовал. Чего он обычно старательно избегает... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба А так попробовать: 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; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 4 минуты назад, adnega сказал: Стоп. Тут же IAR ерунду пишет: 0xC778 STMIA R7!, {R6,R5,R4,R3} Почему? Всё нормально. 3 минуты назад, adnega сказал: А так попробовать: Ну так у меня так и написано. См. первый пост. Почти так. Только Вы зачем-то удалили '&' из первого операнда. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 187 12 апреля, 2023 Опубликовано 12 апреля, 2023 · Жалоба 51 минуту назад, adnega сказал: Не совсем понятно, чем вариант_1 плох? Почему важен именно порядок регистров? ... Стоп. Тут же IAR ерунду пишет: LDM/STM пофигу, в каком порядке перечислен {reglist}. Там в КОП инструкции эти регистры отмечаются битовой маской. А инструкция проходится по списку этих битов, начиная с младшего. Поэтому важен порядок лишь загрузки данных в сами регистры, а не то, как они выглядят в списке LDM/STM. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться