Читаю тут намедни программку на ассемблере, и никак не могу врубиться в одну вещь. Обычный цикл со счетчиком, в один регистр кладется первоначальное значение, в другой – конечное, в начале цикла проверяется, не дошел ли счетчик до конечного значения и выход, если произошёл, в общем, всё как обычно.
Внутри цикла в нескольких местах происходят проверки условий и возврат к началу цикла на следующую итерацию, если условие не выполнилось. Тоже нормально.
Но. Инкрементирование счётчика происходит ПОСЛЕ команды перехода на начало цикла:
li $t9, 0x10000000
li $v1, 0x10000100
LoopStart:
slt $t7, $t9, $v1
beqz $t7, LoopEnd
nop
...
bnel $t5, $t8, LoopStart
addiu $t9, 0x10
...
beqzl $t6, LoopStart
addiu $t9, 0x10
...
LoopEnd:
Я тихо фигею и не понимаю, как это вообще может работать. Но оно работает.
Отгадка нашлась, как обычно, в TFM:
Description: if (rs ≠ rt) then branch_likely
An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is added to the address of the instruction following the branch (not the branch itself), in the branch delay slot, to form a PC-relative effective target address.
If the contents of GPR rs and GPR rt are not equal, branch to the effective target address after the instruction in the delay slot is executed. If the branch is not taken, the instruction in the delay slot is not executed.
То есть, особо умные разработчики процессора, мало того, что позволяют программисту (ну или компилятору) указать, что проверка, скорее всего, окажется успешной (branch_likely), но и зная, что переход обычно вызывает сброс кэша команд, а это минимум один такт процессора, заняли этот такт выполнением команды, написанной после условного перехода. Ну а в тех редких случаях, когда условие не выполнится и переход не произойдёт, следующая команда тихо проигнорируется.
Вот такая оптимизация единичных тактов. А чё, пять старушек – рупь..
Это MIPS, если кому интересно.