Особые ситуации для использования addi vs addiu в MIPS - PullRequest
3 голосов
/ 23 октября 2019

Я прочитал несколько постов об addiu, и я думаю, что было бы полезно понять, если бы я знал некоторые примеры конкретных ситуаций / случаев, когда лучше использовать addiu, а не наоборот, и наоборот (и почему это лучше).

В этом посте говорится о работе с адресами и добавлении адресов. Важно использовать addiu для предотвращения переполнения "ловушки". Но потом кажется, что ловушка даже не очень полезна, так почему я вообще должен использовать addi вместо addiu? Зачем нам использовать addiu вместо addi?

Кажется, мой профессор использует их взаимозаменяемо, чтобы напомнить нам, что addiu существует, может быть ??? Но в конечном итоге меня это смущает, когда я задаюсь вопросом: «Мне действительно нужно использовать addiu в этом случае, или они просто используют его для« развлечения »?»

1 Ответ

1 голос
/ 23 октября 2019

так почему я вообще должен использовать addi вместо addiu?

Как правило, вы не должны этого делать, за исключением того, что assert() не содержит дополнения 2. переполнение со знаком в дополнении. Возможно, вы захотите этого в случаях, когда у вас есть подписанные значения, которые, как вы ожидаете, не переполняются, особенно при записи в asm вручную. (Компиляторы никогда не используют add, всегда addu.)

Инструкции буквально идентичны, кроме того, включая расширение знака 16-битного непосредственного для addiu. (В отличие от ori и других логических значений с нулевым расширением. MIPS для некоторых команд требует нулевого расширения, но расширение знака для addiu означает, что для вычитания маленьких целых чисел также не требуется код операции subiu.)

В программе, предназначенной для вашего собственного использования, сбой вашей программы на неправильных входах может быть лучше, чем позволить что-то переносить, что не должно. Если бы вы писали для Linux MIPS вместо MARS, вы могли бы установить обработчик сигнала для SIGFPE (арифметическое исключение), чтобы вы могли, по крайней мере, напечатать удобное для пользователя сообщение об ошибке, например «Обнаружено переполнение со знаком, прерывание». В MARS я предполагаю, что вы просто попадете в отладчик.


Единственная причина add и addi существует вообще (вместо обычных addu и addiu, который выполняет то, что большинство ISA, таких как x86 и ARM, add) - это целочисленное обнаружение переполнения.

Обнаружение целочисленного переполнения - трудная проблема. (См. Также https://en.wikipedia.org/wiki/Integer_overflow). ISA предпринимали различные попытки предоставить ему аппаратную поддержку, которая позволяла бы программному обеспечению (особенно на высокоуровневых скомпилированных языках) регулярно его использовать. Одной из таких попыток являются инструкции add / sub MIPS. , но не охватывают такие случаи, как сдвиг влево. Поэтому для языков высокого уровня, которые хотели бы обеспечить проверку переполнения для каждой операции, потребуется отдельный механизм. Или они могут просто только использовать другой механизм, а не add / sub вообще.

В отличие от MIPS, рассмотрим ISA, такой как x86 или ARM: у них есть FLAGS или регистр состояния с битом переполнения со знаком, а также способ выполнения условияветвь, если последняя команда установила этот бит. x86 даже имеет команду into (trap, если OF установлено во FLAGS).

Но MIPS вообще не имеет регистра флагов, только целочисленные регистры. негде зафиксировать тот факт, что дополнение переполнилось, кроме как через инструкцию добавления и перехода, такую ​​как add. Не тратя битов на кодированиеДля цели переполнения в случае переполнения единственным реальным выбором является ловушка / исключение.

(Ну, другим более простым в использовании вариантом был бы специальный регистр, который вы можете установить, но тогда переключение контекстасохранить / восстановить его. И это должно было бы работать как JAL для записи, которая добавляет сбой. Это могло потребовать микрокода, которого избегал MIPS. Или, по крайней мере, более сложная обработка этого особого случая, потому что он похож на исключение, но не исключение, и не может быть обнаружен при таких ранних условиях, как beq, потому что для этого нужен полный 32-битный результат сложения.)


Почему архитекторы MIPS назвали обычную инструкцию добавления addu?

Я не знаю, может быть, они были аппаратными людьми, а не программными, и были слишком оптимистичны в том, что они откроютв новую эпоху проверенной арифметики и многих ошибок целочисленного переполнения ушли в прошлое.

Оглядываясь назад, теперь давайте мнемонику add стандартным версиям без перехватов, которые компиляторы всегда используютимел бы самый смысл . Тогда вам понадобится другое имя для версий ловушек. Может быть addt (перехват) или addc (доп проверено). Но addc ужасен из-за путаницы с надстройкой над другими ISA, обычно adc. adds (дополнение подписано) возможно. Называть сложно!


Основы: есть несколько очевидных случаев, когда вам нужно для использования addu:

Для (потенциально) больших целых без знака , очевидно, важно использовать addu. 0x7fffffff + 1 - ничего особенного для неподписанных. Но это переполнение со знаком 2 от INT_MAX до INT_MIN, поэтому add может перехватить.

Для указателей потенциально важно использовать addu. Если вы не используете модель памяти "high half kernel", где один массив не может начинаться ниже 0x80000000 и заканчиваться над ним.


Ниже приведена моя первая версия ответа на этот вопрос. ,Части этого излишни с вышеупомянутым. Но я думаю, что не все. (TODO: закончите редактировать это; у меня сейчас нет времени, но я думаю, что более полезно публиковать сейчас, чем обсуждать это позже.)

, когда лучше использовать addiu, а не addi ии наоборот (и почему так лучше).

Используйте add / addi ТОЛЬКО , если вы специально хотите, чтобы аппарат перехватывал (иначеошибка, вызовите исключение) для переполнения со знаком дополнения 2 (например, обтекание типа INT_MAX + 1 становится INT_MIN).

В большинстве случаев никто этого не хочет, но, возможно, если вы писалиhand in asm и хотел защитить от некоторых ошибок целочисленного переполнения, которые вы могли бы использовать add, если повышение исключения лучше, чем продолжение с неправильным значением. Конечно, это доступно только для add, не сдвиг влево более чем на 1 или другие инструкции. Поэтому, если вам действительно нужна всесторонняя проверка целочисленного переполнения в каком-то защитном коде, вам все равно нужен другой механизм.

И для того, чтобы это было хорошей идеей, вы, вероятно, захотите установить обработчик прерываний для этого аппаратного исключения. (Или в пользовательском пространстве под ОС, обработчик сигнала для SIGFPE, сигнал POSIX для арифметических исключений.)

Или во время разработки, возможно, вы захотите, чтобы целочисленное переполнение просто внезапно останавливалось на инструкции добавления, котораявы ожидали, что не переполнится. то есть как assert. Компиляторы этого не делают, потому что было бы странно иметь иногда только проверки, а не целочисленную арифметику для всего. Но, возможно, лучше, чем ничего.

Одним из мест, где часто могут возникать ошибки переполнения целых чисел, является расчет размера памяти. (Например, в результате небольшого выделения и завершения программы программа может быть ошибкой DOS или ошибкой перезаписи памяти в зависимости от распределителя). Но они часто используют целые числа без знака;0x7fffffff + 1 = 0x80000000

не является ошибкой при выполнении *1116*. В противном случае AWAYS использует addu / addiu

addu - это нормальная инструкция добавления MIPS.

Как указывает Разница между add и addu , имена вводят в заблуждение. u, возможно, соответствует переполнению со знаком как неопределенному поведению в C, но переполнение без знака хорошо определено как перенос. Но, как вы знаете, добавление дополнения 2 - это та же бинарная операция, что и добавление простого без знака. addi просто проверяет переполнение со знаком.

(Не то, чтобы компиляторы C когда-либо использовали add или addi: они не хотят создавать код, который когда-либо дает сбой. Переполнение со знаком - это UB, но этоне означает, что они должны делать что-то конкретное. Кроме того, после оптимизации нередко создается asm с временными значениями, которых нет в абстрактной машине C. Выполнение w + x + y + zпоскольку (w+x)+(y+z) может иметь переполнение со знаком, даже если (((w+x)+y)+z) нет; двоичное добавление действительно ассоциативно, независимо от переполнения со знаком.)


MIPS32r6 даже удаляет addi, оставляя толькобез ошибок addiu. (так что если вы хотите перехватить переполнение со знаком, вы все равно можете поместить операнд в регистр и использовать add.) Это указано в списке в Википедии удаленных редко используемых инструкций в виде инструкций по перехвату целочисленных переполнений с 16-битным немедленным Это должно рассказать вам о том, сколько реального использования addi когда-либо получает.


Мой профессор, кажется, просто использует их взаимозаменяемо, чтобы напомнить нам, что addiu существует, может быть ???

Трудно судить по этому, действительно ли это правда, или у них есть тонкие причины, по которым выне подобрал. например, использовать addi всякий раз, когда это определенно безопасно?

Или когда они думают о целом числе как знаке, даже если код содержит только неотрицательные значения в нем? например, for(int i=0 ; i<100; i++) - это signed int в отношении абстрактной машины C.

В ситуациях, когда известно, что переполнение со знаком невозможно (например, после lui), это не имеет значения,Но ИМО, все равно всегда используйте addiu, если не хотите. Для меня, как человека, читающего код, просмотр add / addi в идеале был бы знаком того, что мы намеренно хотим проверять переполнение со знаком. Но в SO вопросах, чаще всего новички просто использовали add, потому что они не думали или даже не знали о addu. Так что это заставляет вас искать риск «ложноположительных» ошибок ловушки: где add может привести к ошибке, когда вы этого не хотите.


Я не думаю, что есть какие-либо реальныеПроцессоры MIPS, в которых add / addi медленнее, чем addu / addiu. Возможно нет;любой lw или sw может давать сбой в зависимости от входов регистра, поэтому конвейер MIPS должен иметь возможность эффективно обрабатывать команды, которые могут привести к сбоям.

В общем случае они никогда не срабатывают;вы оптимизируете конвейер для этого, и едва ли имеет значение, насколько неэффективным является принятие ошибки. (Или, может быть, это имеет какое-то значение для MIPS с программной обработкой TLB-пропусков; это может происходить довольно часто, гораздо чаще, чем сбои страниц).

...