2016-10-14 4 views
2

В следующем примере:Compile времени Const и формальные параметры

static inline void foo(const int varA) 
{ 
    ... 
    __some_builtin_function(varA); 
    ... 
} 

int main() 
{ 
    foo(10); 
    return 0; 
} 

ли Vara здесь рассматривается как константа времени компиляции?

Обратите внимание, что я работаю с C, а не с C++.

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

ответ

3

Нет, varA - это не постоянная времени компиляции - она ​​может быть разной при каждом вызове функции. Константы имеют определенное определение в стандарте - некоторые ключевые данные касаются в this answer, или вы можете просто прочитать стандарт для официального слова.

Это означает, что вы можете знать, является ли компилятор обрабатывать как константу, в случаях, когда вы вызываете ее с постоянным значением, как в вашем примере. Ответ «да» для любого достойного компилятора с включенной оптимизацией. Вызов вставки и постоянное распространение - это волшебство, которое делает это возможным. Компилятор попытается включить вызов в foo, а затем заменит 10 на аргумент и будет следовать за ним рекурсивно.

Давайте рассмотрим ваш пример. Я немного модифицировал его, чтобы использовать return foo (10) в main, чтобы компилятор не полностью оптимизировал все целиком! Я также выбрал gcc __builtin_popcount как неуказанную функцию, вызванную foo(). Проверьте this godbolt version вашей программы без оптимизации, скомпилированной в gcc 6.2. Сборка выглядит так:

foo(int): 
     push rbp 
     mov  rbp, rsp 
     mov  DWORD PTR [rbp-4], edi 
     mov  eax, DWORD PTR [rbp-4] 
     popcnt eax, eax 
     pop  rbp 
     ret 
main: 
     push rbp 
     mov  rbp, rsp 
     mov  edi, 10 
     call foo(int) 
     pop  rbp 
     ret 

Это просто. Большинство из foo() просто устанавливают фрейм стека и (бессмысленно) нажимают на стек edi (аргумент varA).

Когда мы вызываем foo() из основного, мы передаем 10 в качестве аргумента. Так что ясно, что это константа не помогла.

ОК, давайте составим это с более реалистичным -O2 . Here's what we get:

main: 
     mov  eax, 2 
     ret 

Всё. Все это всего лишь return 2, в значительной степени. Таким образом, компилятор определенно мог видеть, что 10 является постоянным значением и расширяет foo(10). Кроме того, он смог полностью оценить foo(10), вычислив popcount из 10 (0b1010 в двоичном формате) непосредственно, без необходимости инструкции popcount и просто вернув ответ 2.

Также обратите внимание, что компилятор даже не генерировал код для foo(). Это потому, что он может видеть, что объявлено static inline , поэтому его можно вызывать только из этой единицы компиляции и что на самом деле нет вызывающих абонентов, которым нужна полная функция, поскольку единственный сайт вызова был встроен. Так что foo просто исчезает.

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

Ключ здесь заключался в том, что ваш метод foo() объявлен в том же компиляторе, что и его вызывающий, поэтому компилятор может встроить и эффективно оптимизировать две функции. Если бы это было в отдельном блоке компиляции, это не могло произойти, если вы не используете некоторые параметры, такие как генерация кода времени.


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

На самом деле, либо из inline или static достаточно, чтобы локальная функция в модуле компиляции. Однако, если вы опускаете оба, тело из foo() будет создано, поскольку оно может быть вызвано из отдельно скомпилированного модуля. С оптимизацией тело выглядит так:

foo(int): 
     xor  eax, eax 
     popcnt eax, edi 
     ret 
+0

Большое спасибо за такое подробное объяснение. – Arsen

2

Нет, varA не является константой времени компиляции, это просто переменная const int, что означает, что ее значение не может быть изменено внутри функции foo(). Однако компилятор может вывести эту функцию с постоянной времени компиляции 10 в качестве аргумента и скомпилировать версию этой функции, где каждое появление varA заменяется на 10.

 Смежные вопросы

  • Нет связанных вопросов^_^