Это выглядит как отчет о дефекте 1400: Функция равенство указателей решает эту проблему и, как мне кажется, говорит о том, что эту оптимизацию можно делать, но, как показывают комментарии, существуют разногласия. В нем говорится (выделено мной):
Согласно 5.10 [expr.eq], параграф 2, два указателя на функцию сравниваются как равные, только если они указывают на одну и ту же функцию. Однако в качестве оптимизации реализации в настоящее время используют псевдонимы функций, которые имеют идентичные определения. Неясно, должен ли Стандарт явно учитывать эту оптимизацию или нет.
и ответ был:
Стандарт четко определяет требования, и реализации можно оптимизировать в рамках ограничений правила «как если бы».
Вопрос касается двух вопросов:
- Можно ли считать эти указатели равными
- Можно ли объединить функции
Основываясь на комментариях, я вижу две интерпретации ответа:
Эта оптимизация в порядке, стандарт дает реализацию этой свободы в соответствии с правилом «как если бы». Правило как если бы описано в разделе 1.9
и означает, что реализация должна эмулировать только наблюдаемое поведение в соответствии с требованиями стандарта. Это все еще моя интерпретация ответа.
Проблема полностью игнорируется, и в заявлении просто говорится, что корректировка стандарта не требуется, потому что очевидно, что правила «как если бы» охватывают это, но интерпретация предоставляется читателю в качестве упражнения. Хотя я признаю, что из-за краткости ответа я не могу отвергнуть эту точку зрения, в конечном итоге это совершенно бесполезный ответ. Это также кажется несовместимым с ответами в других NAD
проблемах, которые, насколько я могу судить, указывают на проблему, если она существует.
Что говорится в проекте стандарта
Поскольку мы знаем, что имеем дело с правилом как если бы, мы можем начать с него и отметить, что в разделе 1.8
говорится:
Если объект не является битовым полем или подобъектом базового класса нулевого размера, адрес этого объекта является адресом первого занимаемого им байта. Два объекта, не являющиеся битовыми полями, могут иметь один и тот же адрес, если один является подобъектом другого или если хотя бы один является подобъектом базового класса нулевого размера и они относятся к разным типам; в противном случае они должны иметь разные адреса.4
и примечание 4
говорит:
В соответствии с правилом «как если бы» реализации разрешается хранить два объекта по одному и тому же машинному адресу или вообще не сохранять объект, если программа не может заметить разницу.
но примечание из этого раздела гласит:
Функция не является объектом, независимо от того, занимает ли она память так же, как объекты.
хотя это и не является нормативным, требования к объекту, изложенные в параграфе 1
, не имеют смысла в контексте функции и поэтому согласуются с этим примечанием. Таким образом, мы явно ограничены в использовании псевдонимов объектов с некоторыми исключениями, но это ограничение не распространяется на функции.
Далее идет раздел 5.10
Операторы равенства, в котором говорится (выделено мной):
[...] Два указателя сравниваются равными, если они оба равны нулю, оба указывают на одну и ту же функцию или оба представляют один и тот же адрес (3.9.2), в противном случае они сравниваются неравными.
который говорит нам, что два указателя равны, если они:
- Нулевые указатели
- Укажите на ту же функцию
- Представлять тот же адрес
или оба представляют один и тот же адрес, по-видимому, дают достаточно свободы, чтобы позволить компилятору использовать псевдонимы для двух разных функций и не требуют указателей на разные функции для сравнения неравных.
Наблюдения
Кит Томпсон сделал несколько замечательных наблюдений, которые, как мне кажется, стоит добавить к ответу, поскольку они касаются основных вопросов, связанных с этим, говорит он:
Если программа выводит результат &foo == &bar, это наблюдаемое поведение; рассматриваемая оптимизация изменяет наблюдаемое поведение.
с чем я согласен, и если бы мы могли показать, что существует требование, чтобы указатели были неравными, что действительно нарушило бы правило как если бы, но пока мы не можем этого показать.
а также:
[...] рассмотрим программу, которая определяет пустую функцию и использует их адреса как уникальные значения (подумайте о SIG_DFL, SIG_ERR и SIG_IGN в ‹signal.h› / ‹csignal›). Присвоение им одного и того же адреса приведет к поломке такой программы.
Как я отметил в своем комментарии, стандарт C требует, чтобы эти макросы генерировали различные значения из 7.14
в C11:
[...] которые расширяются до постоянных выражений с различными значениями, которые имеют тип, совместимый со вторым аргументом и возвращаемым значением сигнальной функции, и чьи значения не равны адресу любой объявляемой функции[...]
Таким образом, хотя этот случай рассмотрен, возможно, есть и другие случаи, которые могут сделать эту оптимизацию опасной.
Обновить
Ян Хубичка, gcc
разработчик, написал сообщение в блоге Связать время и интер- улучшения процедурной оптимизации в GCC 5, свертывание кода было одной из многих тем, которые он затронул.
Я попросил его прокомментировать, является ли складывание идентичных функций по одному и тому же адресу соответствующим поведением или нет, и он сказал, что это не соответствующее поведение, и действительно такая оптимизация нарушила бы саму gcc
:
Это не соответствует тому, чтобы две функции имели один и тот же адрес, поэтому MSVC здесь довольно агрессивен. Это, например, ломает сам GCC, потому что, к моему удивлению, сравнение адресов выполняется в предварительно скомпилированном коде заголовков. Он работает для многих других проектов, включая Firefox.
Оглядываясь назад, после нескольких месяцев чтения отчетов о дефектах и размышлений о проблемах оптимизации, я склоняюсь к более консервативному чтению ответа комитета. Получение адреса функции является наблюдаемым поведением, поэтому свертывание идентичных функций нарушит правило как если бы.
Обновление 2
Также см. это обсуждение llvm-dev: Равенство указателя функции нулевой длины< /а>:
Это хорошо известная ошибка, нарушающая соответствие в link.exe; LLVM не должен усугублять ситуацию, вводя аналогичную ошибку. Более умные компоновщики (например, я думаю, что и lld, и gold) будут выполнять идентичное комбинирование функций только в том случае, если все функциональные символы, кроме одного, используются только в качестве цели вызовов (а не для фактического наблюдения за адресом). И да, это несоответствующее поведение (редко) ломает ситуацию на практике. См. этот исследовательский документ.
person
Shafik Yaghmour
schedule
23.10.2014
--icf [none,all,safe] Identical Code Folding. '--icf=safe' Folds ctors, dtors and functions whose pointers are definitely not taken.
- person Marc Glisse   schedule 24.10.2014safe
. На самом деле, вы всегда можете сбросить карты, за исключением случаев, когда вы просите адрес, который может создать дополнительные копии (или использовать таблицу переходов). Мне интересно, является ли MSVC технически законным. - person Yakk - Adam Nevraumont   schedule 24.10.2014safe
, потому что они знают, чтоall
является незаконным ( research.google.com/pubs/ pub36912.html). Но я не хочу участвовать в этом судебном споре. - person Marc Glisse   schedule 24.10.2014const
данных, которые логически различны, объединяются в один: в стандарте очень ясно, что разные объекты имеют разные адреса, так что это незаконно. Только после этого недопустимого свертывания реализации двух функций становятся одинаковыми, и ICF на уровне функций приводит к тому, что две функции имеют одно и то же значение. Этот вопрос является допустимым на функциональном уровне. - person Yakk - Adam Nevraumont   schedule 18.03.2015