Разрешено ли компилятору постоянно сворачивать локальную переменную?

25

Рассмотрим этот простой код:

void g();

void foo()
{
    volatile bool x = false;
    if (x)
        g();
}

https://godbolt.org/z/I2kBY7

Вы можете видеть , что ни gccни clangоптимизируют из потенциального вызова g. В моем понимании это правильно: абстрактная машина должна предполагать, что volatileпеременные могут измениться в любой момент (например, из-за аппаратного отображения), поэтому постоянное свертывание falseинициализации в ifпроверку будет неправильным.

Но MSVC полностью исключает вызов g(сохраняя чтение и запись в volatile!). Это стандартное поведение?


Предыстория: я иногда использую этот вид конструкции, чтобы иметь возможность включать / выключать вывод отладочной информации на лету: компилятор должен всегда считывать значение из памяти, поэтому изменение этой переменной / памяти во время отладки должно соответствующим образом изменить поток управления , Выход MSVC перечитывает значение, но игнорирует его (предположительно, из-за постоянного свертывания и / или удаления мертвого кода), что, конечно, противоречит моим намерениям.


Редактирование:

  • Здесь volatileобсуждается исключение операций чтения и записи : разрешено ли компилятору оптимизировать локальную переменную переменной? (спасибо, Натан!) Я думаю, что стандарт совершенно ясно, что эти чтения и записи должны произойти. Но это обсуждение не охватывает вопрос о том, является ли законным компилятор принимать результаты этих чтений как должное и оптимизировать их на этом основании. Я предполагаю, что это не указано / не указано в стандарте, но я был бы счастлив, если бы кто-то доказал, что я неправ.

  • Конечно, я могу создать xнелокальную переменную, чтобы обойти проблему. Этот вопрос больше из любопытства.

Макс Лангоф
источник
3
Это выглядит как очевидная ошибка компилятора для меня.
Сэм Варшавчик
1
Насколько я знаю, это законно, как будто правило. Компилятор может доказать, что даже если объект является изменчивым, его состояние нельзя изменить, чтобы его можно было развернуть. Я не достаточно уверен, чтобы поставить это в ответ, но я чувствую, что это правильно.
Натан Оливер
3
Но я думаю, что аргумент OP о том, что переменная может быть изменена отладчиком, является разумным. Может быть, кто-то должен подать отчет об ошибке в MSVC.
Брайан
2
@curiousguy Даже если вы отбросите результат и / или примете точное значение, вы все равно прочитаете его.
Дедупликатор
2
Интересно, что это только делает для x64. Версия x86 по-прежнему вызывает g () godbolt.org/z/nc3Y-f
Джерри Иеремия

Ответы:

2

Я думаю, что [intro.execution] (номер параграфа может отличаться) можно использовать для объяснения поведения MSVC:

Экземпляр каждого объекта с автоматической продолжительностью хранения связан с каждой записью в его блоке. Такой объект существует и сохраняет свое последнее сохраненное значение во время выполнения блока и пока блок приостановлен ...

Стандарт не разрешает исключение чтения через изменчивое значение, но параграф выше может быть интерпретирован как позволяющий предсказать значение false.


Кстати, Стандарт C (N1570 6.2.4 / 2) говорит, что

Объект существует, имеет постоянный адрес и сохраняет свое последнее сохраненное значение в течение всего времени жизни. 34


34) В случае изменчивого объекта последнее хранилище не обязательно должно быть явным в программе.

Неясно, может ли быть неявное хранилище в объекте с автоматической продолжительностью хранения в памяти C / объектной модели.

Языковой адвокат
источник
Согласитесь, что компилятор может знать, когда
MM
1
Так что, если это правда, то локальные изменчивые объекты (по крайней мере на MSVC) совершенно бессмысленны? Есть ли что-то, что добавление volatileпокупает вас (кроме лишних операций чтения / записи), если оно игнорируется в целях оптимизации?
Макс
1
@MaxLanghof Есть разница между совершенно бессмысленным и не совсем желаемым эффектом.
Дедупликатор
1
@MaxLanghof Промежуточные результаты вычислений с плавающей запятой иногда переносятся в 80-битный регистр, что вызывает проблемы с точностью. Я полагаю, что это gcc-изм, и его можно избежать, объявив все такие двойные числа как изменчивые. Смотрите: gcc.gnu.org/bugzilla/show_bug.cgi?id=323
ForeverLearning
1
@philipxy PS Смотрите мой ответ. Я знаю, что доступ определяется реализацией. Вопрос не в доступе (объект доступен), а в предсказании значения.
Язык юрист
2

TL; DR Компилятор может делать все, что захочет, для каждого изменяемого доступа. Но документация должна сказать вам: «Семантика доступа через volatile glvalue определяется реализацией».


Стандарт определяет для программы разрешенные последовательности «изменчивого доступа» и другого «наблюдаемого поведения» (достигаемого посредством «побочных эффектов»), которые реализация должна соблюдать в соответствии с «правилом« как будто ».

Но стандарт гласит (выделение жирным шрифтом):

Рабочий проект, Стандарт для языка программирования C ++
Номер документа: N4659
Дата: 2017-03-21

§ 10.1.7.1 Cv-квалификаторы

5 Семантика доступа через переменную glvalue определяется реализацией. [...]

Аналогично для интерактивных устройств (выделение жирным шрифтом):

§ 4.6 Выполнение программы

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

7 Наименьшими требованиями к соответствующей реализации являются:

(7.1) - Доступ через изменчивые значения вычисляется строго в соответствии с правилами абстрактной машины.
(7.2) - При завершении программы все данные, записанные в файлы, должны быть идентичны одному из возможных результатов, которые могло бы дать выполнение программы в соответствии с абстрактной семантикой.
(7.3) - Динамика ввода и вывода интерактивных устройств должна происходить таким образом, чтобы запрос на вывод фактически доставлялся до того, как программа ожидает ввода. То, что составляет интерактивное устройство, определяется реализацией.

Все вместе они называются наблюдаемым поведением программы. [...]

(Во всяком случае, какой конкретный код генерируется для программы, стандартом не указано.)

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

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

Хронически люди ошибочно полагают , что для летучих доступов абстрактной оценки машина / чтение вызывает реализованное чтение & задание абстрактной машины / запись вызывает реализованную запись. Нет никаких оснований для этого убеждения, отсутствующей документации реализации, говорящей так. Когда / iff реализация говорит, что она действительно что-то делает при «изменчивом доступе», люди оправдывают ожидание чего-то - возможно, генерации определенного объектного кода.

philipxy
источник
1
Я имею в виду, что это сводится к тому, что «если я придумаю машину, в которой все упомянутые побочные эффекты не работают, то у меня будет легальная реализация C ++, скомпилировав каждую программу в без операции» . Конечно, нас интересуют практически наблюдаемые эффекты, поскольку побочные эффекты абстрактной машины тавтологически абстрактны. Это требует некоторой базовой концепции побочных эффектов, и я бы сказал, что «изменчивый доступ приводит к явным инструкциям доступа к памяти» является частью этого (даже если стандарту все равно), так что я действительно не покупаю «скажем» если вы хотите код вместо абстрактной семантики ". Тем не менее +1.
Макс
Да, качество реализации актуально. Тем не менее, кроме записи в файлы, «наблюдаемые» [sic] определяются реализацией. Если вы хотите иметь возможность устанавливать точки останова, получать доступ к определенной фактической памяти, игнорировать 'volatile' и т. Д. Для абстрактных volatile операций чтения и записи, то вам нужно заставить ваш компилятор выводить соответствующий код . PS В C ++ «побочный эффект» не используется для наблюдения поведения как такового, он используется для описания частичного порядка вычисления подвыражений.
Philipxy
Не хочешь объяснить [так]? Какой источник вы цитируете и в чем ошибка?
Макс
Я просто имею в виду, что наблюдаемая абстрактная машина доступна только в реальном мире, если и как говорит реализация.
Philipxy
1
Вы говорите, что реализация может утверждать, что не существует такого понятия, как интерактивное устройство, поэтому любая программа может делать что угодно, и это все равно будет правильным? (Примечание: я не понимаю вашего акцента на интерактивных устройствах.)
curiousguy
-1

Я считаю, что законно пропускать чек.

Абзац, который все любят цитировать

34) В случае изменчивого объекта последнее хранилище не обязательно должно быть явным в программе

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

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

п. местоимения м.
источник
Можете ли вы объяснить, почему это зло? В показанном коде функция буквально никогда не может быть вызвана.
Дэвид Шварц
@DavidSchwartz Вы можете сделать вывод, что только после указания семантики локальных изменчивых переменных (см. Приведенный выше абзац). Сам стандарт отмечает, что volatileпредполагается, что это является подсказкой реализации, что значение может быть изменено с помощью средств, неизвестных реализации.
Макс
@MaxLanghof Реализация не может правильно обрабатывать что-то неизвестное. На самом деле полезные платформы - это указать, что вы можете и не можете использовать volatileна этой платформе и вне этой спецификации, это всегда будет чушь.
Дэвид Шварц
@DavidSchwartz Конечно, это возможно - следуя семантике (в частности, чтения и записи) абстрактной машины. Возможно, он не сможет правильно оптимизировать - в этом суть стандарта. Теперь, это примечание и, следовательно, не нормативное, и, как мы оба сказали, реализация может указать, что volatileделает, и придерживаться этого. Я хочу сказать, что сам код (в соответствии со стандартной / абстрактной машиной C ++) не позволяет вам определить, gможет ли он быть вызван.
Макс
@DavidSchwartz Код не показывает ничего подобного, потому что отсутствие неявных записей не следует из кода.
нет. местоимения м.