Когда использовать временные и сохраненные регистры на языке ассемблера MIPS? - PullRequest
0 голосов
/ 13 мая 2018

В этом вопросе:

x = x + y - 10 - A [20]

x - $s0
y - $s1
A - $s3

Для ответа я написал:

add   $t0,   $s0,   $s1    # value of x ($s0) + y ($s1) gets stored in temp $t0
addi  $t0,   $t0,  - 10    # subtracts value of $t0 from 10. $t0 now holds the new value
lw    $t1,   80($s3)       # loads value of A[20] into new temp $t1
sub   $s0,   $t0,  $t1     # subtracts values $t0 from $t1 and stores it in x 
                           ($s0)

Однако модельное решение говорит:

add   $s0,   $s0,   $s1   
addi  $s0,   $s0,  - 10    
lw    $t0,   80($s3)   
sub   $s0,   $s0,  $t0

Правильный ли мой первоначальный ответ?Разве это не выводит тот же результат?Если я не прав, объясните, пожалуйста, почему.

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Когда использовать временные и сохраненные регистры на языке ассемблера MIPS?

дополнение к ответу Питера Кордеса.

Общепринятые соглашения о вызовах сборки MIPS требуют, чтобы вы сохраняли значения в «сохраненных» регистрах и позволяли вам изменять «временные» регистры, которые вступают в силу, когда вы вызываете подпрограммы из своего кода.

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

«Сохраненные» регистры должны вместо этого сохраняться вашим кодом, чтобы не изменять их для вызывающей стороны выше, т. Е. Всякий раз, когда вы используете другой новый сохраненный регистр, вы должны поместить его первоначальное значение куда-нибудь (обычно в память стека), а затем восстановить это, прежде чем вернуться к звонящему. Это требует незначительного снижения производительности, поэтому вы можете полностью избежать «сохраненных» регистров в своем коде, если только вы не вызываете несколько подпрограмм, а вместо этого вы получите производительность благодаря наличию значений с более длинным жизненным циклом в сохраненных регистрах без необходимости сохранения / восстановить их при каждом вызове (если они во временном регистре).

Итак, по эмпирическому правилу:

  1. вообще не использовать регистр / значение (исключить с помощью более умного алгоритма или структуры кода, лучше повторно использовать уже затронутые регистры)
  2. использовать временный для значений с ограниченным жизненным циклом, особенно если он не конфликтует с вызовами подпрограмм
  3. использовать сохраненный регистр для значений, которые должны оставаться во время вызовов подпрограммы (особенно нескольких вызовов подпрограммы)

(это определено как "вызывая CONVENTION", то есть вы можете определить + использовать свое собственное соглашение, нарушающее правила использования временного / сохраненного регистра ... это не что-то разработанное внутри CPU)

0 голосов
/ 13 мая 2018

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

Нет недостатка в записи результата в регистр, который инструкция считывает как ввод. Если бы было больше окружающего кода как части большой функции, использование меньшего количества регистров для временных может быть преимуществом. Модельное решение, возможно, является более оптимальным, чем ваше, многократно изменяя $s0 вместо использования большего количества временных значений, если вы рассматриваете давление распределения регистров как часть большой функции.

Но так как нет окружающего кода, нет никаких оснований говорить, что с тобой что-то не так. Может быть, полезно иметь оригинальное значение x дольше.


Перестановка для более короткой задержки критического пути и параллелизм на уровне команд на суперскалярном процессоре . (например, MIPS r10k - суперскаляр в 4 раза с выполнением вне порядка)

Требуются три операции добавления / добавления, и обе версии в вашем вопросе имеют последовательную зависимость, проходящую через все три операции ALU.

2 арифметика дополнения ассоциативна. Но MIPS add дает сбой при переполнении со знаком, поэтому имеет значение, какие временные результаты вы создаете (как для FP с ошибками округления). Но в MIPS также есть addu, который оборачивается вместо сбоя, поэтому, если вам не нужно создавать исключение при переполнении со знаком, используйте addu / addiu / subu, а затем вы можете переупорядочить операции на (x - A[20]) + (y - 10), например.

lw    $t0,   80($s3)       # load as early as possible
addiu $t1,   $s1,  -10     # y-10 in the shadow of the load delay slot
subu  $s0,   $s0,  $t0     # x-A[20]
addu  $s0,   $s0,  $t1

Обратите внимание, что x ($s0) не должен быть готов до 3-й инструкции, поэтому мы можем скрыть некоторую задержку для ввода x. Если вы знаете, из каких инструкций поступают ваши операнды, закажите свои операции так, чтобы последний, который, вероятно, был готов последним, был нужен последним. (Особенно, если вы настраиваете процессор в порядке, в отличие от r10k)

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

...