fastcall
Очевидно он немного быстрее, и в 64-битных системах дофига регистров.
Но ведь язык высокого уровня (С++, например. я скомпилил пустую подпрограмму, а потом посмотрел ассемблерный листинг и мои предположения подтвердились) всегда их перемещает в стек
Кого он помещает? Давайте разбирать на конкретном примере. fastcall вообще придумка разработчиков некоторых компиляторов, о стандартизированности я не слышал.
Про регистры я знаю. Только вот используются в первую очередь rcx, rdx, rsi и rdi, которые затруться при большинстве инструций (умножение, деление, циклы, строковые команды). Не всегда в принцыпе возможно построить процедуру так, чтобы значения сохранились до их последнего использования. Ну а даже в тех случаях, когда это возможно, компилятор всё равно не сможет догадаться как именно это сделать. Поэтому как ни странно компилятор сразуже копирует значения из регистров в стек. Тогда вопрос - зачем их вообще сначала помещать в регистры? Почему бы сразу не запихнуть в стек с помощью push? Или два mov (причём один обязательно направлени в память) быстрее одного push? Маловероятно. По объёму уж точно занимает больше. Тогда какой прирост производительности? Ну или уменьшение размера?
Для проверки я написал программу на C++:
}
Затем скомпилировал 64-битным gcc под Linux. Причём скомпилировал с ключом -S. Это даёт только ассемблерный код. Без создания объектного файла и линкования. Вот асм. код касающийся процедуры a:
.LCFI0:
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.LCFI1:
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl %ecx, -16(%rbp)
movl %r8d, -20(%rbp)
movl %r9d, -24(%rbp)
.loc 1 8 0
leave
ret
Как можно заметить параметры из регистров были скопированы в стек. Зачем же тогда придумали fastcall? Я предполагаю, что просто, что-то не знаю, а на самом деле fastcall даёт прирост производительности...
c = a / 10;
Как сделать, чтобы в c был тоже верный результат? Только путём сохранения переменной в другое место. А ещё из программы может потребоваться вызвать другую... Таким образом я прихожу к выводу, что переменные в 99% случаев всё же придётся сохранить в стеке. Даже если писать программу на ассемблере мало подпрограмм получиться оптимизировать для fastcall (если в ней содержится хотя бы один вызов или команда умножения или деления, то ничего не выйдет). Ну а компилятор - не человек. Он сможет сооптимизировать для fastcall ещё меньше подпрограмм (если учесть, что порядок команд изменять нельзя, то это число приближается к 0).
А если представить себе значительный стек вызовов (что не редкость в современных библиотеках) где над параметрами действий практически не производится (ну разве приведение типа - действие?) а подпрограммы все вызываются и вызываются?
b(p1, p2);
}
void b(int p1, int p2) {
c(p1, p2);
}
...
Да. В таких случаях fastcall действительно немного увеличит производительность...