2016-04-24 4 views
5

В настоящее время я изучаю сборку и языки программирования C, и у меня есть несколько вопросов об этом.Сборка по сравнению с кодом C

C код

int arith(int x, int y, int z) { 
    int t1 = x + y; 
    int t2 = z*48; 
    int t3 = t1 & 0xFFFF; 
    int t4 = t2 * t3; 
    return t4; 
} 

код Ассамблеи

movl 16(%ebp),%eax   z 
leal (%eax,%eax,2), %eax z*3 
sall $4,%eax    t2 = z*48 
movl 12(%ebp),%edx   y 
addl 8(%ebp),%edx   t1 = x+y 
andl $65535,%edx   t3 = t1&0xFFFF 
imull %edx,%eax    Return t4 = t2*t3 

Вместо использования Лил, а затем переход на 4 умножить г на 48, я мог просто использовать imull $ 48,% EAX?

Кроме того, это несколько раз используется регистр% edx. Означает ли это, что t1 перезаписывается? Другими словами, смогу ли я получить t1 прямо перед t4, если захочу?

+1

Да, Нет (переменная 't1' оптимизирована) и №. Для последнего вопроса' x + y' вычисляется, но никогда не сохраняется. _EDX_ имел значение 'x + y' после' addl 8 (% ebp),% edx', но команда 'andl $ 65535,% edx' уничтожает его. Если вы переместили _EDX_ в регистр типа _ECX_ после 'addl 8 (% ebp),% edx', то у вас все равно будет доступ к x + y части вычисления. –

+0

Прохладный. Действительно ли код C был бы переведен за кулисы таким образом, что он не сохранил бы каждую переменную в своем собственном регистре? – Dylan

+2

Нет, если в этом нет необходимости. Это сила оптимизирующего компилятора. – usr2564301

ответ

2

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

Чтобы ответить на ваш первый вопрос, технически это сработает, но компилятор еще раз сделает несколько оптимизаций. Поэтому, хотя может показаться более интуитивным использование imul, компилятор определил, что leal и sall более эффективны. EDIT: Я просто хочу указать, что операторы битового сдвига почти всегда используются вместо imul, когда это возможно. Битовый сдвиг намного дешевле для ЦП, поскольку он буквально просто сдвигает битовые значения, а не пытается выполнить некоторую математическую операцию, которая может занять больше времени процессора.

Теперь о «переписывании» t1. У Ассамблеи нет никакой информации о ваших переменных программы - все, что она знает, это то, что ей нужно выполнить некоторые операции над некоторыми значениями. Хотя сборка потенциально может использовать 4 разных регистра для хранения t1-4, компилятор определил, что это не нужно, и вам нужны только 2 регистра для всех значений. Если вы думаете об этом, это должно иметь смысл. Ваша функция может быть уменьшена до нескольких строк кода. Очевидно, что это не очень хорошая идея, поскольку это сделало бы невозможным чтение, но сборка не обязательно должна быть «удобочитаемой». Если вы вернулись к своей программе и выполнили некоторую другую операцию с t1 перед возвратом t4, вы можете заметить, что ваша сборка отличается от предыдущей и что она может использовать другой регистр, в зависимости от того, как это значение используется.

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

+1

Спасибо. Все, что вы сказали, имело смысл для меня. Это здорово, как он оптимизируется за кулисами. – Dylan

+1

Хороший источник информации по оптимизации инструкций можно найти в этом документе [Agner Fog] (http: //www.agner.орг/оптимизируют/instruction_tables.pdf). В зависимости от архитектуры LEAL может даже не доходить до ALU. На некоторых архитектурах x86 это делается как часть AGU. –