Разрешено ли компилятору C оптимизировать неиспользуемые аргументы функций? - PullRequest
0 голосов
/ 17 декабря 2018

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

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

Но у меня есть вопрос по поводу конкретного случая:
Допустим, есть нетривиальная функция, котораяне может быть встроен или удален, и должен быть вызван, который объявлен без аргументов:

int veryImportantFunc() {
    /* do some important stuff */

    return result;
}

Но эта функция вызывается с аргументами:

int result = veryImportantFunc(1, 2, 3);

Разрешен ли компиляторвызвать функцию, не передавая эти аргументы?

Или есть какие-то стандартные или технические ограничения, которые бы препятствовали такого рода оптимизации?

Кроме того, что если оценка аргумента имеет побочные эффекты:

int counter = 1;
int result = veryImportantFunc(1, ++counter, 3);

Обязан ли компилятор выполнять оценку даже без передачи результата, или было бы законно отбросить оценку, оставив counter == 1?

И, наконец, как насчет дополнительных аргументов:

char* anotherFunc(int answer) {
    /* Do stuff */

    return question;
}

, если эта функция вызывается так:

char* question = anotherFunc(42, 1);

Может ли 1 быть отброшенокомпилятор, основанный на объявлении функции?

РЕДАКТИРОВАТЬ: Чтобы уточнить: я не собираюсь писать код, который есть в моих примерах, и я не нашел этого ни в одном коде, который яЯ работаю над этим.
Этот вопрос предназначен для того, чтобы узнать о том, как работают компиляторы и что говорят соответствующие стандарты, поэтому всем, кто посоветовал мне держаться подальше от такого рода кода: спасибо, но я уже это знаю.

Ответы [ 4 ]

0 голосов
/ 17 декабря 2018

Начнем с того, что "объявлено без аргументов" неправильно.int veryImportantFunc() - это функция, принимающая любые аргументы.Это устаревший стиль C и не должен использоваться.Для функции без аргументов используйте (void).

Разрешено ли компилятору вызывать функцию без передачи этих аргументов?

Если фактическое определение функции не даетсоответствует количеству аргументов, поведение не определено.

Кроме того, что, если оценка аргумента имеет побочные эффекты

Не имеет значения, так как аргументы оцениваются (не указанопорядок) перед вызовом функции.

Обязан ли компилятор выполнять оценку даже без передачи результата, или было бы законно отбросить оценку, оставляя счетчик == 1?

Он будет оценивать аргументы и затем вызывать неопределенное поведение.Может случиться что угодно.

И, наконец, что насчет дополнительных аргументов:

Ваш пример не будет компилироваться, так как он недопустим C.

0 голосов
/ 17 декабря 2018

Допустим, есть нетривиальная функция, которая не может быть встроена или удалена и должна быть вызвана, которая объявлена ​​без аргументов:

int veryImportantFunc() {
   /* do some important stuff */

   return result;
}

Но эта функция вызывается саргументы:

Существует две возможности:

  • функция объявлена ​​с «полным» прототипом, таким как

    int veryImportantFunc(void);
    

    вв этом случае вызов с дополнительными аргументами не будет компилироваться, так как количество параметров и аргументов должно совпадать;

  • функция объявлена ​​как с неопределенным числом аргументов , т.е. объявление , видимое для сайта вызова, равно

    int veryImportantFunc();
    

    , в этом случае вызов является неопределенным поведением, поскольку использование не соответствует фактическому определению функции.

Все остальные соображения по поводу оптимизации не особенно интересны, поскольку то, что вы пытаетесь сделать, является незаконным, однако вы на это смотрите.


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

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

Учитывая, что детали аргументов , передающие , не наблюдаемы 1 , компилятор может в принципе оптимизировать передачу аргументов, в то время как оценка аргументов все еще может потребоваться, если она оказывает некоторое видимое влияние на состояние программы.

При этом мне трудно представить, как такая оптимизация может быть реализована в «классической модели линковки», но с LTCG это не должно быть невозможным.


  1. Единственные наблюдаемые эффекты в соответствии ск стандарту C относятся IO и чтение / запись по volatile переменным.
0 голосов
/ 17 декабря 2018

Следующие цитаты из стандарта C относятся к вашим разным вопросам:

6.5.2.2 Вызовы функций
...
2. Есливыражение, которое обозначает вызываемую функцию, имеет тип, который включает в себя прототип, количество аргументов должно совпадать с количеством параметров.
...
4. Аргумент может быть выражениемлюбой полный тип объекта. При подготовке к вызову функции оцениваются аргументы, и каждому параметру присваивается значение соответствующего аргумента.
...
6. Если выражение, обозначающее вызываемыйФункция имеет тип, который не включает в себя прототип, целочисленные преобразования выполняются для каждого аргумента, а аргументы с типом float повышаются до двойного.Это называется продвижением аргументов по умолчанию. Если число аргументов не равно количеству параметров, поведение не определено. Если функция определена с типом, который включает в себя прототип, и любой из прототипов заканчивается многоточием (, ...) или типы аргументов после продвижения не совместимы с типами параметров, поведение не определено.Если функция определена с типом, который не содержит прототип, и типы аргументов после продвижения не совместимы с типами параметров после продвижения, поведение не определено.
...
10. Существует точка последовательности после вычислений указателя функции и фактических аргументов, но перед фактическим вызовом. Каждая оценка в вызывающей функции (включая другие вызовы функции), которая иначе специально не упорядочена до или послевыполнение тела вызываемой функции имеет неопределенную последовательность относительно выполнения вызываемой функции.

0 голосов
/ 17 декабря 2018

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

int RealSlimShady(void) {
     return Dufus;
}

int MaybeSlimShady(int Mathew, int Mathers) {
    Used(Mathew);
    Used(Mathers);
    return RealSlimShady();
}

Все довольны, и если ваш компилятор того стоит, то будет 0 кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...