В каких случаях компилятору C разрешено игнорировать соглашения о вызовах?

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

Как и когда компилятор может определить, верно ли это для данной функции?

  • Статическая функция видна только другим функциям в той же единице компиляции и, следовательно, является хорошим кандидатом для таких оптимизаций, нарушающих ABI. Но указатели функций на статическую функцию все еще могут быть переданы другим модулям. Пытается ли компилятор определить, передаются ли где-либо в коде указатели на функции?

  • Компилятор gcc имеет некоторые расширения, которые позволяют символы должны быть объявлены как используемые по умолчанию, скрытые или даже внутренние, и в документации особо упоминается, что эта информация может использоваться для выполнения некоторых видов оптимизации, которые невозможны для видимых извне функций. Что произойдет, если указатель функции будет передан внешнему коду для функции, аннотированной как внутренняя?

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


person lxgr    schedule 14.05.2013    source источник


Ответы (2)


Стандарт C требует, чтобы во всех случаях два указателя на одну и ту же функцию сравнивались равными.

Компилятор может выполнять трюки, разрушающие ABI, только если вы объявляете символ как внешний и указатель никогда не передается внешнему коду.

Пытается ли компилятор определить, передаются ли где-либо в коде указатели на функции?

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

Что произойдет, если указатель функции будет передан внешнему коду для функции, аннотированной как внутренняя?

Ничего интересного - должно работать.

Внутренняя/внешняя видимость в основном связана с видимостью. Компилятор может превратить его в лицензию для взлома ABI только в относительно немногих случаях.

Должен ли я просто использовать параметр компилятора, чтобы определить все функции как внутренние и переопределить это с помощью атрибута для всех функций, которые должны быть видны извне?

Если ваша библиотека не состоит только из одной единицы перевода, вы ожидаете, что практически все функции должны быть видны извне. В C нет существенной разницы между «внешним, но только в моей библиотеке» и «внешним для всех». Однако, теоретически, вы можете попробовать это. Однако я сомневаюсь, что вы добьетесь значительного ускорения, и это потребует больших усилий для нетривиальной кодовой базы.

person Puppy    schedule 04.12.2013
comment
В некоторых системах ARM могут возникнуть проблемы совместимости между кодом, который ожидает, что значения с плавающей запятой будут передаваться так же, как и другие параметры, или в регистрах FP. Если бы не указатели на функции, я бы подумал, что проблема может быть решена, если компилятор, который создает код, использующий FPU, создает версию функции с искаженным именем, которая использует регистры FPU, и слабосвязанную оболочку с un -искаженное имя, которое копирует обычные параметры в регистры FPU и вызывает версию с измененным именем. - person supercat; 06.12.2013
comment
Код компилятора, который использует FPU и хочет вызвать версию с регистром FPU, вызовет версию с искаженным именем и слабосвязанную оболочку, которая копирует регистры FPU в обычные параметры и вызывает неискаженную версию. Это должно обеспечить универсальное поведение между любым сочетанием использования FPU вызывающего и вызываемого кода. Что касается указателей на функции, я думаю, что можно было бы избежать проблем дисперсии ABI, сказав, что, если они не объявлены специально, они всегда указывают на методы, которые используют самый простой ABI. Будет ли это работать? - person supercat; 06.12.2013

Статическая функция видна только другим функциям в той же единице компиляции и, следовательно, является хорошим кандидатом для таких оптимизаций, нарушающих ABI. Но указатели функций на статическую функцию все еще могут быть переданы другим модулям. Пытается ли компилятор определить, передаются ли где-либо в коде указатели на функции?

Да, компиляторы выполняют «анализ побега», чтобы увидеть, действительно ли данная функция используется только внутри или она ускользает через свой указатель.

Компилятор gcc имеет несколько расширений, которые позволяют объявлять символы как используемые по умолчанию, скрытые или даже внутренние, и в документации особо упоминается, что эта информация может использоваться для выполнения некоторых видов оптимизации, недоступных для видимых извне функций. Что произойдет, если указатель функции будет передан внешнему коду для функции, аннотированной как внутренняя?

Смотрите мой ответ на ваш первый пункт.

person wilx    schedule 04.12.2013