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

C11 6.7.3 Типовые классификаторы, параграф 7, гласят:

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

В следующем примере объект, доступ к которому в третьей строке относится к указанному выше правилу?

int x;
volatile int *p = &x;
*p = 42;

Другими словами, имеет ли тот факт, что lvalue *p имеет тип volatile int, означает, что к нему относится изменчивый объект, или тот факт, что p указывает на энергонезависимый объект x, означает, что компилятор может оптимизировать эти знания и опустить нестабильный доступ?

Поскольку это может представлять интерес, конкретный случай использования, который меня интересует, выходит за рамки простого C; он включает атомизацию для синхронизации потоков с использованием конструкций pre-C11 (которые могут быть встроенными asm или просто считаться черным ящиком) для атомного сравнения и замены со следующей идиомой:

do {
    tmp = *p;
    new = f(tmp);
} while (atomic_cas(p, tmp, new) != success);

Здесь указатель p будет иметь тип volatile int *, но меня беспокоит то, что происходит, когда фактически выделенный объект является энергонезависимым, в частности, может ли компилятор преобразовать единственный доступ к *p из tmp = *p в два обращения в следующем виде:

do {
    new = f(*p);
} while (atomic_cas(p, *p, new) != success);

что, очевидно, сделает код неправильным. Таким образом, цель состоит в том, чтобы определить, действительно ли все такие объекты с заостренными предметами должны быть volatile int.

Ответ 1

Обновление 18 февраля 2017 года

Ответ ниже приводит цитаты и обсуждает язык в Стандарте, некоторый противоречивый язык в Обосновании и некоторые комментарии от gnu.cc re противоречие. Существует отчет о дефекте, который по существу имеет соглашение о комитете (хотя и все еще открыто), которое должен сказать стандарт, и что намерение всегда было и что реализации всегда отражали, что важна не волатильность объекта (на Standard), а волатильность (lvalue of) доступа (согласно Обоснованию). (Подтвердите Olaf за упоминание этого DR.)

Резюме отчета о дефектах для версии C11 1.10 Дата: апрель 2016 DR 476 летучая семантика для lvalues ​​ 04/2016 Open


Нет. Поскольку доступ к объекту не является изменчивым.

Объект p имеет указатель типа на volatile int. Но x не является объектом нестабильного типа. Квалификации на p влияют на то, какие обращения могут быть сделаны через него, но не влияют на тип объекта, на который он указывает. Нет ограничений на доступ к объекту неквалифицированного типа с помощью летучего lvalue. Таким образом, доступ к x через p не является доступом к объекту с изменчивым квалификационным типом.

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

С другой стороны, этот пост цитирует из 6.7.3 Обоснования для международного стандарта - Языки программирования - C:

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

Однако я не могу найти язык в стандарте, в котором говорится, что семантика основана на типе lvalue. Из gnu.org:

Одной из областей путаницы является различие между объектами, летучих типов и летучих значений. Из стандартной точки C view, объект, определенный с помощью изменчивого типа, внешне внешне поведение. Вы можете думать о таких объектах, как наличие небольшого осциллографа зондов, прикрепленных к ним, чтобы пользователь мог наблюдать некоторые свойства доступа к ним, так же как пользователь может наблюдать данные, записанные в выходных файлов. Однако стандарт не дает понять, пользователи могут наблюдать за доступом с помощью летучих lvalues ​​к обычным объектам.

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

Нет, потому что побочных эффектов нет:

Даже если семантика *p должна быть нестабильной, стандарт все же говорит:

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

Опять же, в вашем коде нет изменчивого объекта. Хотя компиляционная единица, которая видит только p, не может сделать эту оптимизацию.

Также имейте в виду

6.7.3. Спецификаторы типов 7 [...] Что представляет собой доступ к объекту, который имеет тип с изменчивой квалификацией, определяется реализацией.

5.1.2.3 Выполнение программы 8 Более строгие соответствия между абстрактной и фактической семантикой могут быть определены каждой реализацией.

Таким образом, простое появление неустойчивых значений не говорит вам, что такое "доступ". Вы не имеете права говорить о "единственном доступе к *p от tmp = *p", за исключением случаев документированного поведения реализации.

Ответ 2

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

Из C11 (n1570) 6.3.2.1 p1 (сноска опущена, вверху):

lvalue является выражением (с типом объекта, отличным от void), который потенциально обозначает объект; если lvalue не указывает объект при его оценке, поведение undefined. Когда объект имеет определенный тип, тип определяется значением l, используемым для обозначения объекта. [...]

Это значение lvalue, которое определяет тип объекта для определенного доступа. Напротив, *p не обозначает неопределенный объект. Например, там же. 6.7.3 p6 (emph my) читает

[...] Если предпринимается попытка ссылаться на объект определенный с помощью нестабильного типа с использованием значения lval с типом с нестабильной квалификацией, поведение undefined. 133)

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

Если целью было разрешить оптимизацию кода, цитата в вопросе, вероятно, будет прочитана. Объект, который имеет [определяется с помощью] volatile-qualified type, может быть изменен [...]

"Определение" идентификатора *) определено в пункте 6.7, параграф 5.

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

*) Afaik, стандарт не определяет "объект, определенный с (каким-то типом)" в любом месте, поэтому я читаю его как "объект, обозначенный идентификатором, объявленным (некоторым типом) определение".