Будет ли отключение прерываний защищать энергонезависимую переменную или может произойти переупорядочение?

Предположим, INTENABLE - это регистр микроконтроллера, который разрешает / запрещает прерывания, и я объявил его где-то в моих библиотеках как изменчивую переменную, расположенную по соответствующему адресу. my_var - это некоторая переменная, которая изменяется в пределах одного или нескольких прерываний, а также внутри my_func.

Внутри my_func я хотел бы выполнить в my_var некоторую операцию, которая читает, а затем записывает (например, +=) атомарно (в том смысле, что это должно происходить полностью после или до прерывания - прерывание не может произойти, пока оно происходит).

Обычно я получаю что-то вроде этого:

int my_var = 0;

void my_interrupt_handler(void)
{
    // ...

    my_var += 3;

    // ... 
}

int my_func(void)
{
    // ...

    INTENABLE = 0;
    my_var += 5;
    INTENABLE = 1;

    // ...
}

Если я правильно понимаю, если my_var был объявлен volatile, тогда my_var будет гарантированно "чисто" обновлено (то есть прерывание не будет обновлять my_var между чтением и записью my_func), потому что стандарт C гарантирует доступ к энергозависимой памяти происходит по порядку.

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

Мне интересно, потому что я написал аналогичный код (с энергонезависимыми переменными) с той разницей, что я отключаю прерывания с помощью функции из другого модуля компиляции (файла какой-то библиотеки). Если я правильно понимаю, вероятная фактическая причина, которая сработала, заключалась в том, что компилятор не мог предположить, что переменная не читается или не изменяется вызовами вне модуля компиляции. Следовательно, если, скажем, я скомпилировал GCC -flto, изменение порядка за пределами критической области (плохие вещи) может произойти. Я имею это право?


РЕДАКТИРОВАТЬ:

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

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

РЕДАКТИРОВАТЬ 2:

Что касается изменчивого доступа: поскольку я не был уверен в том, было ли переупорядочение изменчивого доступа чем-то, что не разрешено стандартом, чем-то, что было разрешено, но не происходило на практике, или чем-то, что было разрешено и действительно происходило на практике, я подошел с небольшой тестовой программой:

volatile int my_volatile_var;

int my_non_volatile_var;

void my_func(void)
{
    my_volatile_var = 1;
    my_non_volatile_var += 2;
    my_volatile_var = 0;
    my_non_volatile_var += 2;
}

Используя arm-none-eabi-gcc версии 7.3.1 для компиляции с -O2 для Cortex-M0 (arm-none-eabi-gcc -O2 -mcpu=cortex-m0 -c example.c), я получаю следующую сборку:

movs    r2, #1
movs    r1, #0
ldr     r3, [pc, #12]   ; (14 <my_func+0x14>)
str     r2, [r3, #0]
ldr     r2, [pc, #12]   ; (18 <my_func+0x18>)
str     r1, [r3, #0]
ldr     r3, [r2, #0]
adds    r3, #4
str     r3, [r2, #0]
bx      lr

Вы можете ясно видеть, что две my_non_volatile_var += 2 были объединены в одну инструкцию, что происходит после обоих изменяемых обращений. Это означает, что GCC действительно меняет порядок при оптимизации (и я собираюсь продолжить и предположить, что это означает, что это разрешено стандартом).


person tlongeri    schedule 13.12.2018    source источник
comment
Не будет никакой гарантии, что my_var вообще существует.   -  person Eugene Sh.    schedule 13.12.2018
comment
Что за флаг -flto?   -  person Philip    schedule 13.12.2018
comment
Трудно рассуждать об этом, поскольку все, что у вас есть, - это еще один нестабильный механизм защиты. Где и как отключены прерывания? Заменить INTENABLE на очистку / установку глобальной маски прерывания, что ли?   -  person Lundin    schedule 13.12.2018
comment
@EugeneSh. my_var - глобальная переменная. Почему не гарантируется?   -  person Angen    schedule 13.12.2018
comment
@Angen Потому что, если у него нет наблюдаемых побочных эффектов, его можно легко оптимизировать.   -  person Eugene Sh.    schedule 13.12.2018
comment
@Lundin Да, INTENABLE должен быть своего рода глобальной маской прерывания, это то, что я имел в виду. Предположим, INTENABLE - это регистр микроконтроллера, который разрешает / запрещает прерывания. Извините, если это было непонятно   -  person tlongeri    schedule 13.12.2018
comment
Что ж, есть небольшая разница, если это регистр, отключающий прерывания определенного аппаратного периферийного устройства, или если это глобальная маска прерывания. Потому что в последнем случае это должно быть что-то, сводящееся к встроенному ассемблеру, а нормальный компилятор не может действительно переупорядочить или оптимизировать встроенный ассемблер.   -  person Lundin    schedule 13.12.2018
comment
@EugeneSh. Что ж, я не имел в виду, что этот фрагмент кода должен составлять весь код. Я обновлю свой вопрос, чтобы лучше отразить, что некоторые прерывания его изменяют, но, предполагая, что my_var каким-то образом имеет отношение к коду, меня беспокоит то, что он не изменяется между чтением и записью операции += (или, ну, что наблюдаемое поведение в том, что между ними ничего не изменилось).   -  person tlongeri    schedule 13.12.2018
comment
@ TAL33 Мой комментарий все еще в силе. Компилятор может исключить эту переменную, пока вычисление, в котором он используется, по-прежнему дает тот же результат.   -  person Eugene Sh.    schedule 13.12.2018
comment
@Lundin Ах, конечно. Я тупо перепутал оба случая. То, что я сказал, применимо, если я отключаю прерывания на каком-то периферийном устройстве, но, скорее всего, нет, если я делаю это через регистр процессора. Это было очень полезно, спасибо!   -  person tlongeri    schedule 13.12.2018
comment
@Philip Я понимаю, что он в основном оптимизирует границы модуля компиляции (предварительно обработанного файла) (т.е. может оптимизировать, просматривая весь код, а не только один файл за раз). Но не верьте мне на слово, я думаю, вы можете найти лучшее объяснение в поисковой системе.   -  person tlongeri    schedule 13.12.2018
comment
volatile вообще не гарантирует атомарность! Что ж, в любом случае такие вещи оставлены на усмотрение реализации. Но стандарт не гарантирует этого.   -  person Antti Haapala    schedule 13.12.2018
comment
@AnttiHaapala Да, в вопросе я имел в виду то, что я хотел выполнить атомарную операцию над энергонезависимой переменной в том смысле, что она всегда должна происходить до или после того, как прерывания модифицируют переменную. Я обновил свой вопрос, чтобы было понятнее.   -  person tlongeri    schedule 13.12.2018
comment
@AnttiHaapala Обычно выравнивание гарантирует, что простые операции сборки (загрузки, сохранения) являются атомарными, что нет способа сгенерировать код для этих операций, который не является атомарным, что не менее эффективно, ни один компилятор не будет генерировать неатомарные операции загрузки / сохранения ; а ABI требовало выравнивания для всего, что видно отдельно скомпилированному коду.   -  person curiousguy    schedule 14.12.2018
comment
@EugeneSh. Не будет никакой гарантии, что my_var вообще существует Вы говорите, что объект, используемый в обработчике асинхронного сигнала, не гарантированно существует? Звучит неправильно.   -  person curiousguy    schedule 14.12.2018
comment
@curiousguy Компилятор ничего не знает об обработчиках, он просто видит, что функции не вызываются. Если вы хотите сказать ему, что нужно что-то, чего он не видит, вы используете volatile.   -  person Eugene Sh.    schedule 14.12.2018
comment
@EugeneSh. Неправильный. Скажите, как система знает, какую функцию вызывать асинхронно.   -  person curiousguy    schedule 14.12.2018
comment
Я просмотрел сборку библиотеки, которую я использовал, для Cortex-M0, и отключение прерывания для GCC выглядит как __asm volatile ("cpsie i" : : : "memory");, поэтому я информирую себя о том, что означают ключевое слово volatile и "memory" в этом контексте.   -  person tlongeri    schedule 14.12.2018
comment
Система @curiousguy? Или компилятор? Это две очень разные вещи. Компилятор (+ компоновщик) помещает функцию в какое-то место в памяти и инструкцию ветвления в соответствующий вектор прерывания, чтобы перейти к этой функции. Дело сделано. Опять же, функция и инструкция перехода. Никакого понятия обработчика. Отсюда дело до оборудования. Также обратите внимание, что в стандарте C нет понятия прерываний или обработчиков. (кроме очень ограниченного параграфа 5.2.3)   -  person Eugene Sh.    schedule 14.12.2018
comment
@ TAL33 "memory" плохо определен, но интуитивно это означает вызов отдельно скомпилированной функции, которая может получить доступ к любому глобально доступному объекту; он не может получить доступ к локальной переменной, которая не используется совместно с глобальным состоянием.   -  person curiousguy    schedule 14.12.2018
comment
@EugeneSh. Таким образом, компилятор поместил адрес функции в некоторый вектор. Вероятно, это не оптимизирует функцию или что-либо, к чему осуществляется доступ в функции.   -  person curiousguy    schedule 14.12.2018
comment
@curiousguy Будет, если вы не скажете этого не делать. Не забывайте, что обработчик прерывания также может иметь вложенные вызовы функций. Как компилятор может знать, что он тоже не должен оптимизировать их?   -  person Eugene Sh.    schedule 14.12.2018
comment
@EugeneSh. Как компилятор может узнать, что он может что угодно оптимизировать? Считает ли компилятор каким-то образом, что вектор прерывания - это какая-то бесполезная глобальная переменная, которая никогда не используется для каких-либо целей, и что если адрес записывается в вектор, он никогда не используется для каких-либо целей?   -  person curiousguy    schedule 14.12.2018
comment
@curiousguy Прочтите стандартный раздел C 5.1.2.3 и в частности p4 и p6   -  person Eugene Sh.    schedule 14.12.2018
comment
Позвольте нам продолжить это обсуждение в чате.   -  person curiousguy    schedule 14.12.2018


Ответы (3)


C / C ++ volatile имеет очень узкий диапазон гарантированного использования: для прямого взаимодействия с внешним миром (обработчики сигналов, написанные на C / C ++, находятся «снаружи», когда они вызываются асинхронно); вот почему доступ к изменчивым объектам определяется как наблюдаемые, как и консольный ввод-вывод и значение выхода программы (возвращаемое значение main).

Чтобы увидеть это, представьте, что любой изменчивый доступ фактически транслируется посредством ввода-вывода на специальной консоли, терминале или паре устройств FIFO с именами Доступы и Значения, где :

  • непостоянная запись x = v; в объект x типа T преобразуется в запись в FIFO Доступов порядка записи, указанного как 4-кратный ("write", T, &x, v)
  • непостоянное чтение (преобразование lvalue в rvalue) x преобразуется в запись в Доступ трехэтапного ("read", T, &x) и ожидание значения на Значения.

Таким образом, volatile в точности похож на интерактивную консоль.

Хорошей спецификацией volatile является семантика ptrace (которую никто, кроме меня, не использует, но она по-прежнему является самой хорошей изменчивой спецификацией на свете):

  • изменчивая переменная может быть исследована отладчиком / ptrace после того, как программа была остановлена ​​в четко определенной точке;
  • любой доступ к изменчивому объекту представляет собой набор четко определенных точек ПК (счетчика программ), так что там может быть установлена ​​точка останова (**): выражение, выполняющее изменчивый доступ, преобразуется в набор адресов в коде, где нарушение вызывает разрыв в определенное выражение C / C ++;
  • состояние любого изменчивого объекта может быть изменено произвольным образом (*) с помощью ptrace, когда программа остановлена, ограничиваясь только допустимыми значениями объекта в C / C ++; изменение битового шаблона изменчивого объекта с помощью ptrace эквивалентно добавлению выражения присваивания в C / C ++ в четко определенной точке останова C / C ++, поэтому это эквивалентно изменению исходного кода C / C ++ во время выполнения.

Это означает, что у вас есть четко определенное наблюдаемое состояние ptrace изменчивых объектов в этих точках, точка.

(*) Но вы не можете установить для изменчивого объекта недопустимый битовый шаблон с помощью ptrace: компилятор может предположить, что любой объект имеет допустимый битовый шаблон, как определено ABI. Любое использование ptrace для доступа к изменчивому состоянию должно соответствовать спецификации ABI для объектов, совместно используемых с отдельно скомпилированным кодом. Например, компилятор может предположить, что объект изменчивого числа не имеет отрицательного нулевого значения, если ABI этого не допускает. (Очевидно, что отрицательный ноль является допустимым состоянием, семантически отличным от положительного нуля для IEEE float.)

(**) Встраивание и развертывание цикла может генерировать множество точек в ассемблерном / двоичном коде, соответствующих уникальной точке C / C ++; Отладчики справляются с этим, устанавливая множество точек останова на уровне ПК для одной точки останова на уровне источника.

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

[На практике все компиляторы обеспечивают более надежную гарантию, чем семантика ptrace: все изменчивые объекты имеют стабильный адрес, даже если их адрес никогда не используется в коде C / C ++; эта гарантия иногда бывает бесполезной и строго пессимистичной. Более легкая семантическая гарантия ptrace сама по себе чрезвычайно полезна для автоматической переменной в регистре в "сборке высокого уровня".]

Вы не можете проверить работающую программу (или поток), не остановив ее; вы не можете наблюдать ни с одного процессора без синхронизации (такую ​​синхронизацию обеспечивает ptrace).

Эти гарантии действуют на любом уровне оптимизации. При минимальной оптимизации все переменные фактически изменчивы, и программа может быть остановлена ​​при любом выражении.

На более высоком уровне оптимизации вычисления сокращаются, и переменные могут быть даже оптимизированы, если они не содержат полезной информации для какого-либо законного запуска; наиболее очевидным случаем является «квазиконстантная» переменная, которая не объявляется как константа, но используется a-if const: устанавливается один раз и никогда не изменяется. Такая переменная не несет информации во время выполнения, если выражение, которое использовалось для ее установки, может быть пересчитано позже.

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

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

Ловушка (а не ловушка, как в вычислениях) состоит в том, чтобы ожидать семантики, подобной изменчивой Java, по крайней мере в одном процессоре, линейном, упорядоченном семантическом программировании (где по определению нет неупорядоченного выполнения, поскольку есть только POV в состоянии, один-единственный процессор):

int *volatile p = 0;
p = new int(1);

Не существует непостоянной гарантии, что p может быть только нулевым или указывать на объект со значением 1: между инициализацией int и установкой изменчивого объекта не подразумевается изменчивый порядок, поэтому обработчик асинхронного сигнала или точка останова на при назначении volatile может не отображаться int инициализированный.

Но изменчивый указатель не может быть изменен спекулятивно: до тех пор, пока компилятор не получит гарантии, что выражение rhs (правая часть) не вызовет исключения (таким образом, оставит p нетронутым), он не может изменить изменчивый объект (поскольку изменчивый доступ является наблюдаемый по определению).

Возвращаясь к вашему коду:

INTENABLE = 0; // volatile write (A)
my_var += 5;  // normal write
INTENABLE = 1; // volatile write (B)

Здесь INTENABLE является изменчивым, поэтому все обращения наблюдаемы; компилятор должен производить именно эти побочные эффекты; обычные записи являются внутренними по отношению к абстрактной машине, и компилятору нужно только сохранить эти побочные эффекты WRT для получения правильного результата, без учета каких-либо сигналов, которые находятся за пределами абстрактной семантики C / C ++.

С точки зрения семантики ptrace, вы можете установить точку останова в точках (A) и (B) и наблюдать или изменять значение INTENABLE, но это все. Хотя my_var не может быть полностью оптимизирован, поскольку он доступен для внешнего кода (кода передачи сигнала), но в этой функции нет ничего другого, что могло бы получить к нему доступ, поэтому конкретное представление my_var не должно соответствовать его значение согласно абстрактной машине в этот момент.

Другое дело, если у вас есть вызов действительно внешней (не анализируемой компилятором, за пределами «коллективно транслируемого кода») ничего не делающей между ними:

INTENABLE = 0; // volatile write (A)
external_func_1(); // actual NOP be can access my_var 
my_var += 5;  // normal write
external_func_2(); // actual NOP be can access my_var 
INTENABLE = 1; // volatile write (B)

Обратите внимание, что оба этих вызова внешних функций ничего не делают, возможно, ничего не делают:

  • external_func_1() возможно, соблюдает предыдущее значение my_var
  • external_func_2() возможно наблюдает новое значение my_var

Эти вызовы относятся к внешним, отдельно скомпилированным функциям NOP, которые должны выполняться в соответствии с ABI; таким образом, все глобально доступные объекты должны нести ABI-представление своего значения абстрактной машины: объекты должны достичь своего канонического состояния, в отличие от оптимизированного состояния, когда оптимизатор знает, что какое-то конкретное представление в памяти некоторых объектов не достигло ценность абстрактной машины.

В GCC такая внешняя функция, не выполняющая никаких действий, может быть записана либо asm("" : : : "memory");, либо просто asm("");. "memory" неопределенно определен, но ясно означает "доступ ко всему в памяти, чей адрес был утрачен в глобальном масштабе".

[См. Здесь, я полагаюсь на прозрачное намерение спецификации, а не на ее слова, поскольку слова очень часто выбираются неправильно (#) и никем не используются для создания реализации в любом случае, и только мнение людей имеет значение, слов никогда не бывает.

(#) по крайней мере, в мире распространенных языков программирования, где люди не имеют квалификации для написания формальных или даже правильных спецификаций. ]

person curiousguy    schedule 14.12.2018

Без прерываний, я думаю, вы в безопасности от того, что планировщик отключится и что-то изменит вашу переменную за вашей спиной. Но вплоть до мельчайших деталей, это, вероятно, зависит от архитектуры компьютера. Это верно для типичного x86.

Дополнительная проблема с энергонезависимыми переменными заключается в том, что компилятор оптимизирует чтение переменных, если он считает, что он не может измениться, что произойдет с прерываниями в этом разделе или без них. Но если переменная не является изменчивой по своей природе, как входной вывод, это «не должно» нарушать критический раздел.

Краткий ответ: пребывание в критическом разделе не спасет вашу энергонезависимую переменную от оптимизатора.

person Philip    schedule 13.12.2018
comment
Это помечено как встроенное, и OP говорит о микроконтроллерах. Создание x86 не по теме. Какой планировщик? - person Lundin; 13.12.2018
comment
Точно. Вот почему это будет зависеть от архитектуры. И / или какую ОСРВ вы используете. Если у тебя есть. Большинство обрабатывают параллельную обработку через прерывания. самый. - person Philip; 13.12.2018

Здесь есть несколько вещей, вызывающих беспокойство.

Повторный заказ инструкции

Что касается переупорядочения инструкций как части оптимизации, компилятору не разрешается делать это при доступе к изменчивой переменной. Неустойчивая переменная оценивается «строго в соответствии с правилами абстрактной машины», что на практике означает, что в точке последовательности в конце выражения изменчивого доступа все, что находится перед этим выражением, должно быть вычислено.

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

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

Это означает, что содержимое между встроенными инструкциями ассемблера / изменчивым доступом безопасно от переупорядочения по отношению к встроенному ассемблеру / изменчивому доступу, но не по отношению к чему-либо еще.

Оптимизация переменных, совместно используемых с ISR / без видимых побочных эффектов

Ответ на этот вопрос в основном можно найти здесь. В вашем конкретном примере my_var не имеет заметных побочных эффектов и может быть оптимизирован. То же самое происходит, если он изменен из прерывания. Здесь это большая опасность, поскольку встроенные asm / volatile-обращения, окружающие доступ к энергонезависимой переменной, не имеют ни малейшего значения.

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


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

person Lundin    schedule 13.12.2018
comment
Проведя еще несколько исследований, я думаю, что вы ошибаетесь в случае нестабильного доступа. Эта ссылка и this one говорят, что энергонезависимые обращения могут быть переупорядочены вокруг непостоянных обращений. В итоге я сам создал небольшую программу, которая показывает переупорядочивание изменчивых переменных с помощью GCC и -O2: я добавлю это к вопросу в качестве редактирования. - person tlongeri; 13.12.2018
comment
Что касается переупорядочения инструкций как части оптимизации, компилятору не разрешено делать это при доступе к изменчивой переменной. Этот оператор даже не ошибочен: с точки зрения абстрактного C / Семантика C ++, это бессмысленно. Энергозависимая запись не имеет отношения к виртуальной машине: состояние энергонезависимых объектов в этот момент даже не наблюдается. - person curiousguy; 14.12.2018
comment
@curiousguy C17 5.1.2.3 §6 Определение наблюдаемого поведения: Доступ к изменчивым объектам оценивается строго в соответствии с правилами абстрактной машины. Что ранее было определено в §4 как В абстрактной машине все выражения оцениваются в соответствии с семантикой. Так что я не совсем понимаю, что вы имеете в виду. - person Lundin; 14.12.2018
comment
@Lundin 1) Неустойчивые операции выполняются конкретно так, как описано абстрактной машиной. 2) Энергонезависимые операции не наблюдаются. Итак, а) В терминах абстрактной машины все операторы выполняются в соответствии с ... абстрактной машиной. Переупорядочивания нет. б) В реальном программном коде наблюдаются только энергозависимые операции ввода-вывода и exit. Нет никакого переупорядочивания энергонезависимых операций, они даже не должны происходить. Нет наблюдаемых; они всего лишь средство для достижения цели: наблюдаемые операции. - person curiousguy; 14.12.2018
comment
Абстрактная машина - это математический инструмент для описания семантики, его не существует. Я знаю, что стандартный текст очень сбивает с толку, поэтому, вероятно, лучше его забыть и позволить его намерениям объяснить себя. Назначение спецификации C / C ++ состоит в том, чтобы 1) разрешить реализацию любого преобразования, которое сохраняет поведение программы, как любая спецификация высокого уровня 2) разрешить программирование на низком уровне с точным контролем операций, которые взаимодействуют с другими компонентами (аппаратными или программными компонентами) , как и любой язык низкого уровня. Таким образом, спецификация допускает любое преобразование, которое сохраняет след изменчивых операций. - person curiousguy; 14.12.2018
comment
Мы надеемся, что наличие нестабильной операции где-то не предотвращает всех преобразований вокруг нее. Но из-за того, как указывается volatile, у volatile-записи нет даже семантики выпуска для глобальных переменных (или записи в общую память), даже в конфигурации с одним процессором. Получить правильную интуицию в отношении изменчивости непросто. Я тоже это неправильно понимал. - person curiousguy; 15.12.2018
comment
@curiousguy, оцененный в соответствии с семантикой, означает, что все части стандартной маркированной семантики (большая ее часть) являются нормативными и не могут быть проигнорированы. Сюда входят точки последовательности. Когда они появляются в выражении, содержащем изменчивый доступ, они должны вести себя как точки последовательности. Все предыдущие оценки должны быть выполнены до точки последовательности. Это означает, что результат энергонезависимых операций не может сильно перемещаться через эту точку последовательности. - person Lundin; 16.12.2018
comment
Интерпретировать стандарт здесь непросто. Вдобавок поставщики компиляторов любят неверно истолковывать это, потому что они слишком много внимания уделяют оптимизации и недостаточно - безопасности программ. Таким образом, практически все компиляторы были признаны несовместимыми с их реализацией volatile на протяжении многих лет, что привело к обновлениям и исправлениям. - person Lundin; 16.12.2018
comment
@Lundin Все предыдущие оценки должны быть выполнены до точки последовательности Что это будет значить? Применимо ли это к энергонезависимым локальным переменным? - person curiousguy; 16.12.2018
comment
@curiousguy C17 5.1.2.3 §3 Наличие точки последовательности между вычислением выражений A и B подразумевает, что каждое вычисление значения и побочный эффект, связанный с A, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным с B. выражение с изменчивым доступом, то оно должно оцениваться строго в соответствии с этими правилами. Это означает, что A гарантированно будет упорядочен до B, независимо от того, содержит ли A временный доступ или нет. - person Lundin; 17.12.2018
comment
@Lundin Это последовательность, но что это означает конкретно? - person curiousguy; 17.12.2018
comment
@curiousguy То, что я только что написал. - person Lundin; 17.12.2018
comment
@Lundin Так что конкретно это ничего не значит. Абстрактную машину нельзя наблюдать. - person curiousguy; 17.12.2018
comment
@curiousguy Нет, конкретно это означает, что если B - выражение с изменчивым доступом, то оно должно оцениваться строго в соответствии с этими правилами. Это означает, что A гарантированно будет упорядочен до B, независимо от того, содержит ли A временный доступ или нет. - person Lundin; 17.12.2018
comment
@Lundin Как это бетон? Какие переменные наблюдаемы? - person curiousguy; 17.12.2018