Переписываем циклы
Написать более грамотный код можно и в циклах. Стандартная запись с известным числом итераций имеет вид:
uint32_t i = 0;
for (i = 0; i < 127; i++) {}
Очевидно, что для реализации самого цикла на языке ассемблера потребуются следующие три инструкции:
- увеличение
i
на 1 (инструкцияADD
); - сравнение
i
с пороговым значением (127, инструкцияCMP
); - и инструкция продолжения цикла, если
i < 127
(BCC.N
).
Компилятор выведет примерно следующее:
B.N 0x80001f4
ADDS R0, R0, #1
CMP R0, #127
BCC.N 0x80001f2
Однако особенность ARM позволяет реализовать цикл, используя всего две инструкции:
- уменьшение
i
на1
, которая в то же время будет являться условием цикла; - инструкция продолжения цикла, если
i != 0
.
То есть переписать цикл стоит следующим образом:
for (i = 127; i != 0; i--) {}
В таком случае транслятор выдаст следующий ассемблерный код:
B.N 0x8000222
SUBS R1, R1, #1
NE.N 0x8000220
Таким образом можно сэкономить дополнительные 127 операций.
Если число итераций заранее не известно, то поступать стоит следующим образом:
uint32_t n = 127;
for (; n != 0; n--) {
// code here
}
или прибегнуть к конструкции do
—while
.
n = 127;
do {
// code here
} while (--n != 0);
Если использовать for
, проверка n
производится перед выполнением тела цикла, а значит, записав цикл через do
—while
, можно исключить одну лишнюю операцию.
Все эти тонкости зачастую решаются компилятором. Также он способен совершать размотку цикла. Данный метод мы рассматривать не будем.