subu $t0, $zero, $s0
- лучший способ, и именно это делают компиляторы.
В любой конкретной реализации MIPS большинство простых инструкций ALU (add / sub / и / nor) имеют одинаковую производительность.Выполнение той же работы в 1 простой инструкции вместо 2 простых инструкций - это выигрыш в размере кода, задержке и пропускной способности.
Меньше инструкций не всегда лучше, но MIPSклассический RISC ISA не имеет много «медленных» инструкций, кроме mult / div / rem.
sub
вместо subu
вызовет исключение для -INT_MIN
, которого вы избегаетеиспользуя addiu
в версии nor / add.Вы всегда должны использовать u
версию инструкций sub
и add
, если только вы специально не хотите переполнение со знаком для создания исключения.Компиляторы C всегда используют версию u
.(В C переполнение со знаком является неопределенным поведением. Это означает, что допускается ошибка, но не требуется требуется ошибки, и, как правило, этого никто не хочет. Компиляторы хотят иметь возможность оптимизировать и вводить преобразования, которые создают временные значения, которыеникогда не существует в абстрактной машине C, поэтому они должны избегать ошибок при этом.)
В проводнике компилятора Godbolt, MIPS gcc5.4 -O3 компилирует
int neg(int x) { return -x; }
в
neg(int):
j $31
subu $2,$0,$4 # in the branch delay slot
точно так, как мы ожидали.Запрашиваемая компилятором, как правило, является хорошим способом найти эффективные способы сделать что-то в asm.
Сабвуфер IIRC попытается обнаружить переполнение, но будет ли это медленнее?*
Нет.Насколько я знаю, в случае без исключений sub
имеет ту же производительность, что и subu
.
Процессоры сильно оптимизированы для общего случая.Принятие исключения происходит в обычном коде настолько редко, что для исключений достаточно много циклов.Таким образом, ядро ЦП просто должно обнаружить исключение, прежде чем какой-либо неверный результат будет записан обратно в файл реестра или сохранен в кэш / память.В любом конвейере MIPS существует как минимум пара каскадов конвейера между исполнением и обратной записью.
В случае переполнения со знаком ALU может генерировать сигнал переполнения в том же цикле, что и результат.(ISA с регистром «flags», который обновляется большинством инструкций, делают это все время как часть нормальной операции инструкции add
: если программное обеспечение хочет сделать что-то особенное при переполнении со знаком на x86 или ARM, они используютусловная ветвь на флаге переполнения (OF на x86, V на ARM). MIPS отличается тем, что трудно что-либо сделать, кроме как сделать исключение при подписанном переполнении.)