Как узнать, согласен ли gcc с тем, что что-то нестабильно?

Рассмотрим следующее:

volatile uint32_t i;

Как узнать, относился ли gcc к переменной i или нет? Он будет объявлен как таковой, потому что никакой соседний код не собирается его изменять, а его изменение, вероятно, связано с каким-то прерыванием.

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

Если взять следующий глупый код:

#include <stdio.h>
#include <inttypes.h>

volatile uint32_t i;

int main(void)
{
        if (i == 64738)
                return 0;
        else
                return 1;
}

Скомпилируйте его в объектный формат и дизассемблируйте через objdump, затем сделайте то же самое, удалив 'volatile', разницы нет (согласно diff). Является ли объявление volatile слишком близким к тому месту, где оно проверено или изменено, или я должен всегда использовать некоторый атомарный тип при объявлении чего-либо volatile? Влияют ли на это какие-то флаги оптимизации?

Заметьте, мой дурацкий образец не полностью соответствует моему вопросу, я это понимаю. Я только пытаюсь выяснить, обрабатывал ли gcc переменную как volatile или нет, поэтому я изучаю небольшие дампы, чтобы попытаться найти разницу.


person Tim Post♦    schedule 13.03.2009    source источник
comment
Программа имеет неопределенное поведение (доступ к неинициализированной переменной), я думаю.   -  person MSalters    schedule 13.03.2009
comment
В вопросе говорится: глупый образец. Со стороны пуристов можно было бы добавить extern.   -  person Anonymous    schedule 13.03.2009
comment
@MSalters, это не программа, это фрагмент, который я быстро набрал. Я знаю, что я не инициализирован, и от него зависит условие.   -  person Tim Post♦    schedule 13.03.2009
comment
@unknown, я думаю, ты имеешь в виду s/extern/static ? 'i' определено в этом модуле.   -  person Tim Post♦    schedule 13.03.2009
comment
нет никакой разницы, может означать одно из двух: (а) ошибка компилятора полностью игнорирует volatile - это было бы плохо. Или (б) компилятор бережно относится к volatile, пропуская любые оптимизации, которые могут его нарушить. Затем, когда вы скомпилировали его без volatile, компилятор использовал точно такой же набор оптимизаций, пропуская (хотя это и не требовалось) оптимизации, которые нарушали бы volatile. Это совершенно нормально — мы ожидаем, что это произойдет при компиляции без оптимизации.   -  person David Cary    schedule 14.03.2011


Ответы (7)


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

Чтобы быть на 100% уверенным, и для такого простого примера проверьте вывод сборки.

person Anonymous    schedule 13.03.2009
comment
Вывод ассемблера с ключевым словом volatile или без него в этом простом примере одинаков, что заставляет меня задаться вопросом, подчиняется ли gcc объявлению или решает, будет ли переменная изменена. Итак, как я могу сказать по выходным данным сборки? - person Tim Post♦; 13.03.2009
comment
Попробуйте добавить некоторые флаги оптимизации, должна появиться небольшая разница. - person Anonymous; 13.03.2009
comment
БЛАГОДАРНОСТЬ! :) Я должен подчиняться CFLAGS, которые пользователь устанавливает при компиляции, поэтому я рву на себе волосы, чтобы узнать, что что-то изменчивое было обработано как обычное. - person Tim Post♦; 13.03.2009
comment
К сожалению, в стандарте не ясно указано, что дух C, признанный в уставе и обосновании, предполагает, что компиляторы хорошего качества не должны оптимизировать вещи для volatile доступов таким образом, чтобы это мешало программистам делать то, что должно быть сделано, и должны полагаться на то, что программисты склонны знать больше, чем компиляторы, о том, что должно произойти, чтобы программа соответствовала требованиям. - person supercat; 28.11.2018
comment
Я не думаю, что использование volatile_id_int в связанной статье адекватно для предотвращения всех возможных оптимизаций в случаях, когда изменчивая запись может повлиять на обычные объекты. Например, если запись с уточнением volatile используется для обновления нижней половины unsigned, умный компилятор может превратить *(unsigned short volatile*)&thing=1; y=thing; в unsigned short volatile*temp = (unsigned short volatile*)&thing; y=thing; *temp=1;. Что необходимо, так это возможность для разработчиков компиляторов признать, что некоторые виды оптимизации часто опасны и бесполезны. - person supercat; 28.11.2018

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

По сути, когда вы объявляете что-то изменчивым, вы говорите компилятору не выполнять определенные оптимизации. Если он решил не делать эти оптимизации, вы не знаете, знаете, что он их не сделал, потому что он был объявлен изменчивым, или просто решил, что эти регистры нужны ему для чего-то другого, или он не сделал этого. не заметил, что это может превратить его в константу времени компиляции.

person Paul Tomblin    schedule 13.03.2009

Насколько я знаю, volatile помогает оптимизатору. Например, если ваш код выглядел так:

int foo() {
    int x = 0;
    while (x);
    return 42;
}

Цикл while будет оптимизирован из двоичного кода.

Но если вы определите 'x' как изменчивый (т.е. volatile int x;), то компилятор оставит цикл в покое.

person David Wolever    schedule 13.03.2009

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

Чтобы показать разницу, вам понадобятся избыточные нагрузки и/или хранилища. Попробуйте что-то вроде

int i = 5;
int j = i + 2;
i = 5;
i = 5;
printf("%d %d\n", i, j);

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

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

person David Thornley    schedule 13.03.2009

Он должен всегда считать его изменчивым.

Причина того, что код один и тот же, заключается в том, что volatile просто указывает компилятору загружать переменную из памяти каждый раз, когда он к ней обращается. Даже при включенной оптимизации компилятору по-прежнему необходимо один раз загрузить i из памяти в написанном вами коде, потому что он не может вывести значение i во время компиляции. Если вы будете обращаться к нему повторно, вы увидите разницу.

person Jesse Rusak    schedule 13.03.2009
comment
Так и должно быть, но компиляторам разрешено игнорировать подсказку. Я не думаю, что GCC игнорирует подсказку, но им это разрешено. - person Max Lybbert; 14.03.2009
comment
Для протокола: Ханс Бем написал очень полезный документ о volatile и его невозможности использования на современном оборудовании по адресу open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html . - person Max Lybbert; 18.06.2011
comment
@MaxLybbert Должен, но компиляторам разрешено игнорировать подсказку. Чепуха. Конечно, им нельзя игнорировать volatile. - person curiousguy; 26.10.2011
comment
@curiousguy Я кое-чему научился за два года. Документ Бёма, на который я ссылался, содержит некоторую полезную информацию, как и cs. utah.edu/~regehr/papers/emsoft08-preprint.pdf . Важно отметить, что volatile почти определенно не означает, что думает программист (см. ссылку Boehm). Даже когда компилятор делает все правильно, современные ЦП все равно умудряются все напортачить (или, как выразился Бём, большинство реализаций не вставляют достаточных ограничений памяти, чтобы гарантировать, что другие потоки или даже аппаратные устройства будут видеть volatile операций в том же порядке, что и которые они были выпущены). - person Max Lybbert; 28.10.2011

Любой современный компилятор имеет несколько этапов. Один из довольно простых, но интересных вопросов — правильно ли проанализировано объявление самой переменной. Это легко, потому что изменение имени C++ должно различаться в зависимости от изменчивости. Следовательно, если вы скомпилируете дважды, один раз с определением volatile, таблицы символов должны немного отличаться.

person MSalters    schedule 13.03.2009
comment
AFAICT, все, поскольку может быть много переменных с одним и тем же именем. Они должны быть в разных областях, поэтому изменение имен должно включать как минимум эту область. - person MSalters; 27.10.2011

Прочтите стандарт, прежде чем неправильно цитировать или ставить минус. Вот цитата из n2798:

7.1.6.1 Квалификаторы cv

7 Примечание: volatile — это подсказка реализации, чтобы избежать агрессивной оптимизации с участием объекта, поскольку значение объекта может быть изменено средствами, не обнаруживаемыми реализацией. Подробную семантику см. в 1.9. В общем, семантика volatile должна быть такой же в C++, как и в C.

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

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

Это ключевое слово находит применение в setjmp и longjmp типах обработки ошибок. Единственное, что вы должны иметь в виду, это то, что вы указываете ключевое слово volatile, когда считаете, что переменная может измениться. То есть можно было взять обычный предмет и обойтись несколькими слепками.

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

Если вы действительно хотели компилировать другую сборку с оптимизацией

person dirkgently    schedule 13.03.2009
comment
Точно не намек. См. стандарты C и C++ по наблюдаемому поведению. float — это подсказка по сравнению с volatile. - person MSalters; 13.03.2009
comment
Точно. Прочтите все - стандарт определяет, что представляет собой изменчивый доступ к реализации. - person dirkgently; 13.03.2009
comment
@MSalters: обновлено с цитатой. - person dirkgently; 13.03.2009
comment
Да, компиляторы могут игнорировать подсказку. - person Max Lybbert; 14.03.2009
comment
это не так. они должны читать из памяти и записывать в память каждый раз, когда вы действуете на нее. что отличается (в C), так это то, что он позволяет компилятору оптимизировать часть выражения, где его значение не используется и не появляется необходимый побочный эффект (даже изменчивый!). однако нужно очень расплывчато. - person Johannes Schaub - litb; 14.03.2009
comment
поэтому я думаю, что компилятор на самом деле не может выполнять какие-либо оптимизации, потому что то, что необходимо, зависит от программы и ее цели. поэтому, если у вас есть локальная переменная i, которая является изменчивой, int a = (1, i, 2); значение i отбрасывается, но все еще читается из памяти. - person Johannes Schaub - litb; 14.03.2009
comment
Тогда почему в стандарте сказано, что volatile — это подсказка, а не volatile — это команда? - person Max Lybbert; 15.03.2009
comment
Для протокола: Ханс Бем написал очень полезный документ о volatile и его невозможности использования на современном оборудовании по адресу open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html . - person Max Lybbert; 18.06.2011
comment
Ключевое слово volatile действует как подсказка. Очень похоже на ключевое слово регистрации Неправильно. - person curiousguy; 26.10.2011
comment
Тогда почему в стандарте говорится, что volatile — это подсказка, а не volatile — это команда? std говорит о многих вещах. Hans Boehm написал очень полезный документ о volatile и его невозможности использования на современном оборудовании И что? - person curiousguy; 26.10.2011
comment
@MaxLybbert: было бы точнее описать отсутствие volatile как намек на то, что компилятор может удовлетворять запросы на чтение содержимым последнего запроса или информацией, полученной в любое время с момента фактической последней записи. выполняются и могут откладывать запросы на запись до следующего раза, когда информация будет считана из хранилища. Использование volatile отменяет это разрешение. - person supercat; 18.02.2017