Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > C/C++: Общие вопросы > Соглашения вызовов и оптимизация.


Автор: applegame 04.03.18, 16:53
Я недавно участвовал в беседе. И там обсуждался вопрос calling convention.
Известно, что если функция видима из других единиц трансляции, то компилятор должен придерживаться определенного ABI, которое обычно документируется.
А если функция "невидима", то может ли компилятор менять эти соглашения по своему усмотрению. И сталкивались ли вы со случаями, когда скажем GCC или MSVC применял для таких функций что-то иное, чем всякие stdcall, thiscall, fastcal и прочие xxxcall? Кроме известного инлайнинга.

Автор: Qraizer 06.03.18, 14:51
Судя по молчанию, никто не встречал подобного. Я тоже не встречал. Вероятно, это слишком накладно с точки зрения реализации этого типа оптимизации при не весьма очевидном профите. Встраивание вызовов даёт такой профит, и тут всё хорошо в плане оптимизаций и их реализации, но вот невстроенный вызов... тут, пожалуй, имеет смысл только лишь оптимизация стартовых адресов функций для повышения эффективности кеша и их взаимное относительное расположение для улучшения статистики кеша переходов. Это даст очевидный профит, на фоне которого жонглирование параметрами ABI будет просто незаметно.

Добавлено
Цитата applegame @
А если функция "невидима" ...
Не вполне точный термин. Есть internal linkage и no linkage. Ты какой имел в виду? Если последний, то я даже теряюсь, как для функций его можно бы получить в реальности.

Автор: ЫукпШ 06.03.18, 15:04
Цитата Qraizer @
Судя по молчанию, никто не встречал подобного.

Нечто похожее я встречал - кросс-компилер IAR иногда вообще вставлял функцию как "inline".
А иногда наоборот - произвольно "вычленял" кусок из некой процедуры и делал этот кусок
отдельной процедурой для общего использования.
Turbo-C Borland 1988г умеет заменять far-процелуру на near в одном модуле.
---
Теоретически такое возможно, но чтобы это выяснить надо подробно изучать
ассемблерные листинги. Для микроконтроллеров это может иметь практический смысл.
А для Виндус это не очень актуально..
Могу добавить, что иногда VC Микрософт произвольно и по непонятному алгоритму
начинает менять взаимное расположение (последовательность следования) процедур,
при микроскопическом изменении в исходниках.
х/з зачем.

Автор: amk 06.03.18, 19:22
В принципе применяемое по умолчанию соглашение близко к оптимуму - первые пара параметров передаются через регистры, остальные в стеке. Так что смысла менять его не много. Точки входа большинство компиляторов выравнивают уже на первой ступеньке оптимизации. Замена вызова на встраивание производится иногда даже для глобально видимых функций, в зависимости от её сложности. Так же объявленная inline функция, если оказывается слишком сложной для встраивания может быть скомпилирована, как нормальная и вызвана. Ограничено число встраиваний при рекурсивных вызовах. С некоторого не очень глубокого уровня вызывается отдельно расположенная функция.

Автор: applegame 06.03.18, 19:41
Цитата Qraizer @
Не вполне точный термин. Есть internal linkage и no linkage. Ты какой имел в виду?
Что такое no linkage, я не знаю. Я про internal linkage. Например глобальные функции объявленные как static.
И о чем толкует чувак?
Цитата
It is a "convention" for a reason. Everybody has to follow the convention or you couldn't call your function from another module.

However, if the function is not visible then GCC has options. It may inline the function or call it however it wants to. It might even split it into "hot" and "cold" parts and inline the hot code path. That usually only happens when building with profile guided optimization.

If you want GCC to make optimizations like that, work on hiding your functions. If you are building an executable look at -fwhole-program. If you are building libraries look at -fvisibility=hidden. Also look into -flto.

отсюда:
https://stackoverflow.com/questions/2233124...ing-conventions

Автор: amk 06.03.18, 19:51
Кстати, вспомнил интересную оптимизацию вызовов в Turbo C 2.0. В моделях памяти large и medium (в которых код каждой единицы трансляции размещался в отдельном сегменте) при вызове функции внутри единицы трансляции (в том же сегменте) компилятор использовал не ожидаемый CALL FAR PTR, а загонял в стек регистр кода (PUSH CS), после чего делал CALL NEAR PTR. Чем экономил байт памяти и несколько тактов процессора.

Автор: Qraizer 06.03.18, 20:24
Цитата applegame @
Я про internal linkage. Например глобальные функции объявленные как static.
Даже на такие функции ты можешь взять указатель и куда-то передать. Тип linkage относится только к именам сущностей, по которым к ним можно обращаться явно. Косвенный метод доступа этим критериям неподвластен. Фактически linkage является именно характеристикой имени, а не самой сущности.

Добавлено
Цитата applegame @
Что такое no linkage, я не знаю.
Цитата 3.5 Program and linkage
2 A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope:
  • When a name has external linkage, the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.
  • When a name has internal linkage, the entity it denotes can be referred to by names from other scopes in the same translation unit.
  • When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.


Добавлено
Например, no linkage будут просто обычные локальные переменные. Но вот функцию no linkage я как-то с трудом представляю.

Автор: applegame 07.03.18, 08:10
Цитата Qraizer @
Даже на такие функции ты можешь взять указатель и куда-то передать.
Интересный вопрос. Соглашения о вызовах, насколько я понимаю, отданы на откуп компиляторам, а значит просто брать указатель на функцию и куда-то там передавать достаточно рискованно. Нужно согласовывать соглашения.

Автор: Qraizer 07.03.18, 11:45
Вот именно. Пожалуй, это самый главный аргумент, чтобы компиляторы не пытались мутить с нестандартностью соглашений. Т.б. какой модификатор у указателя имеется, поинтер на именно такую функцию в него и можно вфигачить. Так что любые такие соглашения должны быть документированы.

Автор: applegame 07.03.18, 16:35
Тем не менее разные компиляторы мутят по своему соглашения.

Автор: Mr.Delphist 15.03.18, 12:57
Соглашение о вызове важно тогда, когда приходится на стык двух разных компиляторов (или сессий компилирования одним и тем же компилятором). До тех пор пока мы в рамках одного проекта, оптимизатор волен делать всё что угодно.

Автор: ЫукпШ 15.03.18, 17:36
Цитата Mr.Delphist @
До тех пор пока мы в рамках одного проекта, оптимизатор волен делать всё что угодно.

А вызовы функций из dll ?
Полагаю, что правильнее такая логика:
1. Везде, где cоглашение о вызове указано явно в исходнике, компилер не должен его менять.
2. Там, где явного указания не имеется, компилятор может решить вопрос по своему усмотрению.

Автор: Mr.Delphist 18.03.18, 22:03
Цитата ЫукпШ @
А вызовы функций из dll ?

Это и есть тот самый "стык" - соглашение о вызове важно, противоречащие ему оптимизации - низзя :blink:

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)