Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Могут ли функции иметь один адрес?

#cpp #c #language_lawyer


В ответе говорится, что Open Watcom компилирует такой код

bool f(bool var1)
{
  bool var2 = !var1;
  return var2;
}

bool g(bool var1)
{
  bool var2;
  if (var1) var2 = 0;
  else var2 = 1;
  return var2;
}


следующим образом

bool near f( bool ):
L$1:
    test        al,al 
    sete        al 
    ret         

bool near g( bool ):
    jmp         L$1 


Т. е. из функции g делается jmp в начало функции f.

Получается, что у функции g есть лишний jmp по сравнению с f.


Зачем это нужно и почему нельзя было просто саму функцию g совместить с f?

bool near f( bool ):
bool near g( bool ):
    test        al,al 
    sete        al 
    ret         

Что на эту тему говорит стандарт? Например, есть ли там пункт, что указатели на две
различные функции обязаны быть различными? Если есть, то что он даёт?
Какие плюсы от такой реализации?

    


Ответы

Ответ 1



На основе дискуссии здесь: Нет, адреса не должны быть равными Стандарт, 5.10/2.2-3, гласит: — Otherwise, if the pointers are both null, both point to the same function, or both represent the same address ([basic.compound]), they compare equal. — Otherwise, the pointers compare unequal. Компилятор имеет право, однако, сделать их равными по as-if rule, то есть, если это не влияет на видимый результат работы программы. Поэтому даже если конкретный компилятор совместил функции в отсутствие получения и сравнения их адресов, это ещё не нарушение стандарта. (Компилятор вообще имеет право выкинуть все функции, если этого никто не заметит.) По поводу возможного практического использования этого правила. Пусть у нас есть контейнер функций (указателей на функции) — например, std::unordered_set — с условием уникальности. Если два независимых участка кода добавляют в него локально определённую, невидимую другим участкам кода функцию, они могут быть уверены, что функция имеет уникальный адрес и будет реально добавлена. В противном случае добавление локально определённой функции могло бы и не сработать, если в другом месте другой код добавил локальную функцию с таким же кодом. Ещё один пример того, когда несклеивание функций полезно, из упомянутого обсуждения: SIG_IGN и SIG_DFL обязаны быть различными адресами. Имплементация стандартной библиотеки вполне может использовать пустые функции void ign(int) { } и void dfl(int) { }. «Склеивание» этих функций сломает код.

Ответ 2



Он делает это с оптимизацией по умолчанию. Вы поспешили с вопросом - я скомпилировал тот же код с максимальной оптимизацией, и он благополучно слил функции в одну (добавил в оригинальный ответ): Module: G:\Tmp\test.c GROUP: 'DGROUP' CONST,CONST2,_DATA Segment: _TEXT PARA USE32 00000009 bytes 0000 f_: 0000 g_: 0000 85 C0 test eax,eax 0002 0F 94 C0 sete al 0005 0F B6 C0 movzx eax,al 0008 C3 ret Routine Size: 9 bytes, Routine Base: _TEXT + 0000 Подозрение, что полная оптимизация - многопроходная, а по умолчанию - проход только один. Доказать не могу :), но такая гипотеза объясняет это поведение...

Комментариев нет:

Отправить комментарий