Для чего используется ключевое слово fastcall в Visual C?
Я видел fastcall
обозначение добавлено перед многими функциями. Почему это используется?
2 ответа
Это обозначение перед функцией называется "соглашением о вызовах". Он определяет, как (на низком уровне) компилятор будет передавать входные параметры функции и извлекать ее результаты после выполнения.
Есть много различных соглашений о вызовах, самым популярным из которых является stdcall
а также cdecl
,
Вы можете подумать, что есть только один способ сделать это, но на самом деле, есть десятки способов, которыми вы можете вызывать функцию и передавать переменные внутрь и наружу. Вы можете поместить входные параметры в стек (push, push, push для вызова; pop, pop, pop для чтения входных параметров). Или, возможно, вы бы предпочли засунуть их в регистры (это fastcall
- он пытается подогнать некоторые входные параметры в регистрах скорости).
Но тогда как насчет заказа? Вы толкаете их слева направо или справа налево? Как насчет результата - всегда есть только один (при условии отсутствия ссылочных параметров), поэтому вы помещаете результат в стек, в регистр, по определенному адресу памяти?
Кроме того, давайте предположим, что вы используете стек для связи - кто на самом деле выполняет очистку стека после вызова функции - вызывающий или вызываемый?
Как насчет резервного копирования и последующего восстановления содержимого (определенных) регистров ЦП - должен ли вызывающий сделать это, или же вызывающий будет гарантировать, что он вернет все так, как было?
Самое популярное соглашение о вызовах (безусловно) cdecl
, который является стандартным соглашением о вызовах в C и C++. WIN32 API использует stdcall
, что означает, что любой код, который вызывает API-интерфейс WIN32, должен использовать stdcall
для этих вызовов функций (что делает его еще одним популярным выбором).
fastcall
это немного странно - люди реализовали для многих функций только с одним параметром in/out, загрузка и извлечение из стека, основанного на памяти, довольно трудоемки и делают вызовы функций немного тяжелыми, поэтому различные компиляторы представили (разные) соглашения о вызовах, которые поместят один или несколько параметров в регистры, прежде чем поместить остальные в стек для лучшей производительности. Проблема в том, что не все компиляторы использовали одни и те же правила для того, что и где, а кто что делает с fastcall
и в результате вы должны быть осторожны при его использовании, потому что вы никогда не будете знать, кто что делает. Наконец, посмотрите, действительно ли fastcall быстрее? для информации о fastcall
преимущества производительности.
Сложные вещи.
Необходимо помнить кое-что важное: не добавляйте и не изменяйте соглашения о вызовах, если вы точно не знаете , что делаете, потому что, если вызывающий и вызываемый стороны не согласны с соглашением о вызовах, вы, вероятно, в конечном итоге с повреждением стека и segfault. Это обычно происходит, когда у вас есть функция, вызываемая в DLL/ разделяемой библиотеке, и написана программа, которая зависит от того, является ли DLL/SO/dylib определенным соглашением о вызовах (скажем, cdecl
), затем библиотека перекомпилируется с другим соглашением о вызовах (скажем, fastcall
). Теперь старая программа больше не может общаться с новой библиотекой.
Соглашения, называемые fastcall, не были стандартизированы и реализованы по-разному, в зависимости от поставщика компилятора. Обычно соглашения о вызовах fastcall передают один или несколько аргументов в регистрах, что уменьшает количество обращений к памяти, необходимых для вызова.