fbpx
14.8 C
София

LDM е моята любима ARM инструкция

Оригиналът е на Vladimir Keleshev или може би Владимир Келешев

Най-четени

Даниел Десподов
Даниел Десподовhttps://www.kaldata.com/
Новинар. Увличам се от съвременни технологии, информационна безопасност, спорт, наука и изкуствен интелект.

LDM или load multiple е моята любима инструкция в асемблера за ARM архитектурите. Ето защо.

Първо, да разберем какво върши тази процесорна инструкция. Ето пример:

ldm r4, {r0, r1, r2, r3}

В този пример участват базовият регистър r4 и набор регистри, в случая {r0, r1, r2, r3}. Действията са следните: От адреса, указан в базовия регистър последователно се зареждат думи в регистрите от набора. Действието на тази инструкция може да се демонстрира с помощта на следния С-подобен псевдокод:

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-битова маска. Ето как може съвсем опростено да бъде показани тези 16 бита:

Тази инструкция е идеална за архитектурите от типа 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) се прехвърлят чрез регистрите. Ето защо е изключително важно да има ефективни, като се наричат в ARM архитектурата, пролог и епилог. За щастие, FP и LR могат да бъдат записани наведнъж с помощта на само една процесорна инструкция, като се използва стандартния ARM пролог:

push {fp, lr}

А след това те могат да бъдат възстановени и да се осъществи връщане (за епилога):

pop {fp, lr}
bx lr

Има и по-добро решение – можете да ги възстановите и да се върнете само с една инструкция:

pop {fp, pc}

Тук значението на адреса на връщане (LR) директно се поставя програмния брояч (PC) и не е необходимо да се извършва самото завръщане!

Това си е много добре, но в ARM е възможно едновременно с FP и LR да бъдат записани няколко аргумента в стека, което е полезно, ако например техните адреси са заети:

push {r0-r3, fp, lr}

Със съвсем същата инструкция можем да запишем FP и LR и едновременно с това да заделим известно стеково пространство:

push {r0-r3, fp, lr}

Идеята тук е, че ‘пушваме’ в стека регистрите r0-r3 не толкова, за да запишем техните значения, а за да преместим указателя на стека с четири думи напред.

ARM64

Подозирам, че когато е настъпило времето да се разработи 64-битовата версия на процесорните инструкции на ARM архитектурата, трябвало е да се направи компромис и броят на регистрите просто да се удвои до 32 броя. Спомням си, че имаше статия, в която се казваше, че тази промяна е подобрила производителността с 6% във всички аспекти. Само че когато имаме 32 регистъра, няма как да се използва същата маска за инструкциите с дължина 32 бита. Ето защо, в ARM64 вместо LDM и STM се появиха техните духовни наследници LDP и STP: load pair и store pair.

Този пост първоначално започна като обикновена нишка в Twitter.


Откъсът е от блога на Vladimir Keleshev, където могат да бъдат намерени маса интересни неща.


Коментирайте статията в нашите Форуми. За да научите първи най-важното, харесайте страницата ни във Facebook, и ни последвайте в Telegram и Viber или изтеглете приложението на Kaldata.com за Android, iOS и Huawei!

Абонирай се
Извести ме за
guest

2 Коментара
стари
нови оценка
Отзиви
Всички коментари

Нови ревюта

Подобни новини