Ответ 1
Есть разница между C и С++ для этого вопроса, который объясняет, что происходит.
лязг-3.4
При компиляции любого из этих фрагментов как С++, испускаемая сборка не ссылалась на s в любом случае. Фактически для обоих выпущено предупреждение:
volatile.c:8:2: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
s;
Эти предупреждения не были выданы при компиляции в режиме C99. Как упоминалось в этом сообщении в блоге и эта запись в вики GCC из комментариев комментариев, используя s
в этом контексте вызывает преобразование lvalue-to-rvalue в C, но не в С++. Это подтверждается рассмотрением Clang AST для C, так как существует ImplicitCastExpr из LvalueToRValue, который не существует в AST, сгенерированном с С++. (AST не зависит от размера структуры).
Быстрый grep источника Clang обнаруживает это при испускании агрегатных выражений:
case CK_LValueToRValue:
// If we're loading from a volatile type, force the destination
// into existence.
if (E->getSubExpr()->getType().isVolatileQualified()) {
EnsureDest(E->getType());
return Visit(E->getSubExpr());
}
EnsureDest
заставляет испускать слот стека, размер и тип для выражения. Поскольку оптимизаторам не разрешено удалять изменчивые обращения, они остаются в виде скалярной нагрузки/хранилища и memcpy соответственно как в IR, так и в asm. Это поведение, которое я ожидал бы, учитывая приведенное выше.
GCC-4.8.2
Здесь я наблюдаю то же поведение, что и в вопросе. Однако, когда я изменяю выражение от s;
до s.dummy;
, доступ не отображается ни в одной из версий. Я не знаком с внутренними системами gcc, поскольку я с LLVM, поэтому не могу предположить, почему это произойдет. Но, основываясь на вышеупомянутых наблюдениях, я бы сказал, что это ошибка компилятора из-за несогласованности.