[Программирование] LDM. Моя любимая инструкция ARM (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
LDM — или load multiple — моя любимая инструкция в ассемблере для ARM. Вот почему.
Во-первых, что она делает. Вот пример:
ldm r4, {r0, r1, r2, r3}
Здесь она принимает базовый регистр (в данном случае r4) и набор регистров (в данном случае {r0, r1, r2, r3}). Загружает последовательные слова из адреса в базовом регистре в регистры из набора. Действие инструкции можно продемонстрировать с помощью такого C-подобного псевдокода:
r0 = r4[0];
r1 = r4[1];
r2 = r4[2];
r3 = r4[3];
Немало заданий для одной инструкции! Именно поэтому она называется load multiple.
Нотация набора также допускает диапазоны. Мы можем переписать предыдущий пример следующим образом:
ldm r4, {r0-r3}
В наборе разрешены все 16 регистров ARM. Итак, законно следующее:
ldm r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}
В 32-битной инструкции набор регистров кодируется как 16-битная маска. Вот упрощённая кодировка исходного примера:
Упрощённое кодирование инструкции LDM
Такая инструкция идеально подходит для архитектуры load-store, такой как ARM, где основной рабочий процесс выглядит так:
- загрузить множество значений из памяти в регистры,
- выполнить операции исключительно с регистрами,
- сохранить результаты из регистров обратно в память.
Противоположностью LDM находится STM — store multiple.
Копирование блоков
С помощью этих двух инструкций можно быстро копировать большие блоки памяти. Вы можете скопировать восемь слов (или 32 байта!) памяти всего двумя инструкциями:
ldm r0, {r4-r11}
stm r1, {r4-r11}
У LDM и STM также есть варианты автоинкремента (обозначаемые знаком “!”), где базовый регистр увеличивается на количество загруженных/сохранённых слов, так что можно копировать в быстром цикле:
ldm r0!, {r4-r11}
stm r1!, {r4-r11}
Реализация стеков
В ARM инструкция POP — это просто псевдоним для LDM с указателем стека (и автоинкрементом). Она выглядит и работает точно так же:
ldm sp!, {r0-r3}
pop {r0-r3}
А инструкция PUSH — это псевдоним для варианта STM (STMDB).
Вы можете делать push и pop, копируя регистры в большом объёме в стек и из него за один раз. А если заменить SP другим регистром, то сможете реализовать эффективные стеки в других областях памяти. Например, вы можете реализовать теневой стек в куче.
Сохранение регистров
Вы боитесь использовать регистры, сохранённые вызовом (call-preserved), потому что их требуется сохранить, а в любом случае можно использовать слот стека? Это больше не проблема, потому что можно за один раз сохранить все call-preserved регистры, которые вы хотите использовать:
push {r4-r11}
Пролог и эпилог
В ARM первые четыре аргумента, обратный адрес (LR) и указатель кадра (FP) передаются в регистрах. Вот почему особенно важно иметь эффективные пролог и эпилог. К счастью, вы можете сохранить FP и LR за один раз, используя довольно стандартный пролог ARM:
push {fp, lr}
А потом восстановить их и вернуться (для эпилога):
pop {fp, lr}
bx lr
Ещё лучше, вы можете всё восстановить и вернуться за один раз!
pop {fp, pc}
Тут значение обратного адреса (LR) вставляется в регистр счётчика программ (PC), так что не нужен явный возврат!
Это достаточно хорошо само по себе, но можно одновременно отправить некоторые аргументы в стек (например, если их адрес занят):
push {r0-r3, fp, lr}
Или можно сохранить FP и LR и одновременно выделить некоторое пространство в стеке:
push {r0-r3, fp, lr}
В этом случае мы пушим r0-r3 не для их значения, а чтобы продвинуть указатель стека на четыре слова.
ARM64
Подозреваю, когда пришло время разработать 64-битную версию набора команд ARM, пришлось пойти на компромисс и удвоить количество регистров до 32-х. Помню, в какой-то статье говорилось, что это изменение улучшает производительность примерно на 6% по всем направлениям. С 32-мя регистрами больше невозможно закодировать битовую маску всех регистров в 32-битную длинную инструкцию. Таким образом, вместо ARM64 появились LDP и STP: load pair и store pair, духовные преемники LDM и STM.
Этот пост изначально начинался как тред в твиттере.
===========
Источник:
habr.com
===========
===========
Автор оригинала: Vladimir Keleshev
===========Похожие новости:
- [Тестирование IT-систем, Программирование, TDD] С чего начинаются тесты
- [JavaScript, Программирование] Из каталога NPM удалили четыре зловредных пакета
- [Разработка игр] Сборка ArmorPaint из исходников
- [Программирование, Совершенный код, C++, Rust, История IT] Интереснейшее влияние Cyclone (перевод)
- [PHP, Программирование, Яндекс API] Эволюция PHP — от 5.6 до 8.0 (Часть 1) (перевод)
- [JavaScript, Программирование, Разработка веб-сайтов] Настройка Webpack 5 с нуля (перевод)
- [Python, Программирование] О полезности contextvars
- [JavaScript, Программирование, Java, TypeScript] TypeScript для бэкенд-разработки (перевод)
- [Программирование, Разработка мобильных приложений, Разработка под Android] Большие картинки? Deal with it
- [Процессоры, IT-компании] Аллен Ву — «пешка», ставшая «конем» в шахматной партии США и Китая
Теги для поиска: #_programmirovanie (Программирование), #_arm, #_arm64, #_ldm, #_stm, #_assembler (ассемблер), #_arhitektura_loadstore (архитектура load-store), #_tenevoj_stek (теневой стек), #_ldp, #_stp, #_programmirovanie (
Программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:27
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
LDM — или load multiple — моя любимая инструкция в ассемблере для ARM. Вот почему. Во-первых, что она делает. Вот пример: ldm r4, {r0, r1, r2, r3}
Здесь она принимает базовый регистр (в данном случае r4) и набор регистров (в данном случае {r0, r1, r2, r3}). Загружает последовательные слова из адреса в базовом регистре в регистры из набора. Действие инструкции можно продемонстрировать с помощью такого C-подобного псевдокода: r0 = r4[0];
r1 = r4[1]; r2 = r4[2]; r3 = r4[3]; Немало заданий для одной инструкции! Именно поэтому она называется load multiple. Нотация набора также допускает диапазоны. Мы можем переписать предыдущий пример следующим образом: ldm r4, {r0-r3}
В наборе разрешены все 16 регистров ARM. Итак, законно следующее: ldm r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}
В 32-битной инструкции набор регистров кодируется как 16-битная маска. Вот упрощённая кодировка исходного примера: Упрощённое кодирование инструкции LDM Такая инструкция идеально подходит для архитектуры load-store, такой как ARM, где основной рабочий процесс выглядит так:
Противоположностью LDM находится STM — store multiple. Копирование блоков С помощью этих двух инструкций можно быстро копировать большие блоки памяти. Вы можете скопировать восемь слов (или 32 байта!) памяти всего двумя инструкциями: ldm r0, {r4-r11}
stm r1, {r4-r11} У LDM и STM также есть варианты автоинкремента (обозначаемые знаком “!”), где базовый регистр увеличивается на количество загруженных/сохранённых слов, так что можно копировать в быстром цикле: ldm r0!, {r4-r11}
stm r1!, {r4-r11} Реализация стеков В ARM инструкция POP — это просто псевдоним для LDM с указателем стека (и автоинкрементом). Она выглядит и работает точно так же: ldm sp!, {r0-r3}
pop {r0-r3} А инструкция PUSH — это псевдоним для варианта STM (STMDB). Вы можете делать push и pop, копируя регистры в большом объёме в стек и из него за один раз. А если заменить SP другим регистром, то сможете реализовать эффективные стеки в других областях памяти. Например, вы можете реализовать теневой стек в куче. Сохранение регистров Вы боитесь использовать регистры, сохранённые вызовом (call-preserved), потому что их требуется сохранить, а в любом случае можно использовать слот стека? Это больше не проблема, потому что можно за один раз сохранить все call-preserved регистры, которые вы хотите использовать: push {r4-r11}
Пролог и эпилог В ARM первые четыре аргумента, обратный адрес (LR) и указатель кадра (FP) передаются в регистрах. Вот почему особенно важно иметь эффективные пролог и эпилог. К счастью, вы можете сохранить FP и LR за один раз, используя довольно стандартный пролог ARM: push {fp, lr}
А потом восстановить их и вернуться (для эпилога): pop {fp, lr}
bx lr Ещё лучше, вы можете всё восстановить и вернуться за один раз! pop {fp, pc}
Тут значение обратного адреса (LR) вставляется в регистр счётчика программ (PC), так что не нужен явный возврат! Это достаточно хорошо само по себе, но можно одновременно отправить некоторые аргументы в стек (например, если их адрес занят): push {r0-r3, fp, lr}
Или можно сохранить FP и LR и одновременно выделить некоторое пространство в стеке: push {r0-r3, fp, lr}
В этом случае мы пушим r0-r3 не для их значения, а чтобы продвинуть указатель стека на четыре слова. ARM64 Подозреваю, когда пришло время разработать 64-битную версию набора команд ARM, пришлось пойти на компромисс и удвоить количество регистров до 32-х. Помню, в какой-то статье говорилось, что это изменение улучшает производительность примерно на 6% по всем направлениям. С 32-мя регистрами больше невозможно закодировать битовую маску всех регистров в 32-битную длинную инструкцию. Таким образом, вместо ARM64 появились LDP и STP: load pair и store pair, духовные преемники LDM и STM. Этот пост изначально начинался как тред в твиттере. =========== Источник: habr.com =========== =========== Автор оригинала: Vladimir Keleshev ===========Похожие новости:
Программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:27
Часовой пояс: UTC + 5