Для чего используется ключевое слово fastcall в visual c?
-
12-12-2019 - |
Вопрос
Я видел, как fastcall
обозначения, добавляемые перед многими функциями.Почему он используется?
Решение
Это обозначение перед функцией называется "соглашением о вызове". Оно определяет, как (на низком уровне) компилятор будет передавать входные параметры функции и извлекать ее результаты после ее выполнения.
Существует множество различных соглашений о вызовах, наиболее популярными из которых являются stdcall
и cdecl
.
Вы можете подумать, что есть только один способ сделать это, но на самом деле существуют десятки способов вызвать функцию и передавать переменные внутрь и наружу.Вы могли бы поместить входные параметры в стек (push, push, push для вызова;хлоп, хлоп, хлоп для считывания входных параметров).Или, возможно, вы предпочли бы поместить их в регистры (это fastcall
- он пытается подогнать некоторые входные параметры в регистры для ускорения).
Но тогда как насчет заказа?Вы двигаете их слева направо или справа налево?Как насчет результата - всегда есть только один (при условии отсутствия ссылочных параметров), так помещаете ли вы результат в стек, в регистр, по определенному адресу памяти?
Кроме того, давайте предположим, что вы используете стек для связи - чья работа заключается в том, чтобы фактически очистить стек после вызова функции - вызывающей стороны или вызываемого объекта?
Как насчет резервного копирования и последующего восстановления содержимого (определенных) регистров процессора - должен ли вызывающий это делать, или вызываемый объект гарантирует, что вернет все так, как было?
Самым популярным соглашением о вызове (на сегодняшний день) является cdecl
, которое является стандартным соглашением о вызове как в C, так и в C++.WIN32 API использует stdcall
, что означает, что любой код, вызывающий WIN32 API, должен использовать stdcall
для этих вызовов функций (что делает его еще одним популярным выбором).
fastcall
это немного странно - люди поняли, что для многих функций только с одним параметром ввода / вывода нажатие и извлечение из стека, основанного на памяти, требует довольно больших накладных расходов и делает вызовы функций немного тяжелыми, поэтому разные компиляторы ввели (разные) соглашения о вызовах, которые будут размещать один или несколько параметров в регистрах перед помещением остальных в стек для повышения производительности.Проблема в том, что не все компиляторы использовали одни и те же правила для определения того, что куда идет и кто что делает с fastcall
, и в результате вы должны быть осторожны при его использовании, потому что вы никогда не будете знать, кто что делает.Наконец, смотрите Действительно ли fastcall быстрее? для получения информации о fastcall
преимущества в производительности.
Сложная штука.
Что-то важное, о чем нужно помнить:не добавляйте и не изменяйте соглашения о вызовах, если вы не знаете именно так что вы делаете, потому что, если и вызывающий, и вызываемый объект не согласны с соглашением о вызове, вы, скорее всего, столкнетесь с повреждением стека и ошибкой сегмента.Обычно это происходит, когда у вас есть функция, вызываемая в DLL/разделяемой библиотеке, и написана программа, которая зависит от того, что DLL/SO/dylib является определенным соглашением о вызове (скажем, cdecl
), затем библиотека перекомпилируется с другим соглашением о вызове (скажем, fastcall
).Теперь старая программа больше не может взаимодействовать с новой библиотекой.
Другие советы
Соглашения, озаглавленные fastcall, не были стандартизированы и были реализованы по-разному, в зависимости от поставщика компилятора.Обычно соглашения о вызове fastcall передают один или несколько аргументов в регистрах, что уменьшает количество обращений к памяти, необходимых для вызова.