Чи дозволено компілятору постійно складати локальну мінливу?


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
Наскільки я знаю, це законно за правилом ніби. Компілятор може довести, що незважаючи на те, що об'єкт є непостійним, немає можливості змінити його стан, щоб його можна було скласти. Я недостатньо впевнений, щоб сказати це на відповідь, але вважаю, що це правильно.
NathanOliver

3
Але я думаю, що аргумент ОП про те, що змінна може бути змінена налагоджувачем, є розумним. Можливо, хтось повинен подати звіт про помилку в MSVC.
Брайан

2
@curiousguy Навіть якщо відкинути результат та / або припустити точне значення, ви все одно прочитали його.
Deduplicator

2
Цікаво, що це робиться лише для x64. Версія x86 все ще називає g () godbolt.org/z/nc3Y-f
Джеррі Джеремія

Відповіді:


2

Я думаю, що [intro.execution] (номер абзацу змінюється) може бути використаний для пояснення поведінки MSVC:

Екземпляр кожного об'єкта з автоматичною тривалістю зберігання пов'язаний з кожним записом у його блок. Такий об’єкт існує і зберігає своє останнє збережене значення під час виконання блоку і під час призупинення блоку ...

Стандарт не дозволяє усунути прочитане через непостійний glvalue, але абзац вище може бути інтерпретований як такий, що дозволяє передбачити значення false.


До речі, про це говорить стандарт C (N1570 6.2.4 / 2)

Об'єкт існує, має постійну адресу і зберігає своє останнє збережене значення протягом усього життя. 34


34) Що стосується летючого об'єкта, останній склад не повинен бути явним у програмі.

Незрозуміло, чи не могло існувати явне зберігання в об'єкті з автоматичною тривалістю зберігання в пам'яті C / об'єктній моделі.


Погодьтеся, що компілятор може знати, коли можливі не явні магазини на цільовій платформі
MM

1
Тож якщо це правда, то локальні мінливі об'єкти (принаймні на MSVC) цілком безглузді? Чи є щось, що додавання volatileкупує вам (крім зайвих читає / пише), якщо воно ігнорується з метою оптимізації?
Макс Ленгоф

1
@MaxLanghof Існує різниця між цілком безглуздим і не таким ефектом, який ти хочеш / очікуєш.
Deduplicator

1
@MaxLanghof Проміжні результати обчислень з плаваючою комою іноді потрапляють у 80-бітний регістр, що спричиняє проблеми з точністю. Я вважаю, що це є gcc-ізмом, і цього можна уникнути, оголосивши всі такі подвійні як мінливими. Дивіться: gcc.gnu.org/bugzilla/show_bug.cgi?id=323
ForeverLearning

1
@philipxy PS Дивіться мою відповідь Я знаю, що доступ визначено реалізацією. Питання не в доступі (доступ до об'єкта), а в передбаченні значення.
юрист з мови

2

TL; DR Компілятор може робити все, що завгодно, при кожному непостійному доступі. Але документація повинна вам сказати .-- "Семантика доступу через мінливий glvalue визначається реалізацією."


Стандарт визначає для програми дозволені послідовності "нестабільних доступу" та інших "поведінки, що спостерігаються" (досягнуто за допомогою "побічних ефектів"), що реалізація повинна дотримуватися "правила" як "якщо".

Але стандарт говорить (мій наголосний наголос):

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

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

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

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

§ 4.6 Виконання програми

5 Відповідна реалізація, що виконує добре сформовану програму, повинна виробляти таку саму поведінку, що спостерігається, як і одне з можливих виконання відповідного екземпляра абстрактної машини з тією ж програмою та тим самим входом. [...]

7 Найменшими вимогами щодо відповідної реалізації є:

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

Вони разом називаються спостережуваною поведінкою програми. [...]

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

Отже, хоча стандарт говорить, що нестабільний доступ не може бути усунутий від абстрактних послідовностей побічних ефектів абстрактних машин та наслідків, що спостерігаються, що визначений деяким кодом, ви не можете очікувати, що щось відображатиметься в об'єктному коді чи реальному світі поведінка, якщо документація на ваш компілятор не говорить про те, що становить нестабільний доступ . Діто для інтерактивних пристроїв.

Якщо ви зацікавлені в мінливості щодо абстрактних послідовностей побічних ефектів абстрактних машин та / або наслідків, що спостерігаються, які деякі коди (можливо) визначають, тоді говорите так . Але якщо вас цікавить, який відповідний об'єктний код генерується, ви повинні інтерпретувати це в контексті вашого компілятора та компіляції .

Хронічно люди помилково вважають, що для мінливих доходів абстрактне машинне оцінювання / зчитування викликає реалізоване читання, а абстрактне машинне призначення / запис викликає реалізовану запис. Немає підстав для такої думки, що це відсутня документація щодо впровадження. Коли / якщо Iff реалізує, говорить, що вона фактично робить щось при "мінливому доступі", люди виправдовуються, очікуючи, що щось - можливо, генерація певного об'єктного коду.


1
Я маю на увазі, що це зводиться до "якщо я придумаю машину, де всі згадані побічні ефекти відсутні, тоді я маю легальну реалізацію C ++, компілюючи кожну програму до неоперації" . Звичайно, нас цікавлять практично помітні ефекти, оскільки абстрактні побічні ефекти машини є тавтологічно абстрактними. Для цього потрібна якась основна концепція побічних ефектів, і я можу пояснити, що "нестабільний доступ призводить до явних інструкцій доступу до пам'яті" є частиною цього (навіть якщо стандарт не має значення), тому я не купую "скажіть" якщо ви хочете код замість абстрактної семантики ". Все-таки +1.
Макс Ленгоф

Так, якість виконання є актуальною. Тим не менш, крім запису у файли, "спостережувані" [sic] визначені реалізацією. Якщо ви хочете мати змогу встановити точки перерви, отримати доступ до певної фактичної пам'яті, ігнорувати "непостійні" тощо на абстрактних непостійних зчитуваннях і записах, тоді вам доведеться отримати письменника-компілятора для виведення відповідного коду . PS В C ++ "побічний ефект" не використовується для спостереження за поведінкою як такий, він використовується для опису часткового порядку оцінки підвиражень.
Філіпсі

Хочете пояснити [sic]? Яке джерело ви цитуєте і в чому помилка?
Макс Лангхоф

Я маю на увазі, що абстрактна машина, яку можна спостерігати, спостерігається лише в реальному світі, якщо & як каже, що це реалізація.
Філіпсі

1
Ви хочете сказати, що реалізація може стверджувати, що не існує такого поняття, як інтерактивний пристрій, тому будь-яка програма може робити що завгодно, і все одно це буде правильно? (Примітка: я не розумію вашого акценту на інтерактивних пристроях.)
curiousguy

-1

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

Абзац, який всі люблять цитувати

34) Що стосується летючого об'єкта, останній склад не повинен бути явним у програмі

не означає, що впровадження повинно припускати, що такі сховища можливі в будь-який час або для будь-якої мінливої ​​змінної. Реалізація знає, які магазини можливі. Наприклад, цілком доцільно припустити, що такі неявні записи трапляються лише для мінливих змінних, відображених у регістри пристроїв, і що таке відображення можливе лише для змінних із зовнішнім зв’язком. Або реалізація може припускати, що такі записи існують лише у місцях пам'яті, розміщених у форматі слова.

Сказавши це, я думаю, що поведінка MSVC - це помилка. Немає реальних причин оптимізувати виклик. Така оптимізація може бути сумісною, але це непотрібно зло.


Чи можете ви пояснити, чому це зло? У показаному коді функцію буквально ніколи не можна викликати.
Девід Шварц

@DavidSchwartz Ви можете зробити лише висновок, що після вказівки семантики локальних змінних змінних (див. Цитований параграф вище). Сам стандарт зазначає, що volatileце має бути натяком на реалізацію, що значення може змінюватися засобами, невідомими реалізації.
Макс Ленгоф

@MaxLanghof Немає можливості, щоб реалізація могла правильно обробити щось невідоме. Що насправді корисні платформи - це вказати, що ви можете, а що не можете використовувати volatileна цій платформі та поза цією специфікацією, це завжди буде шумом.
Девід Шварц

@DavidSchwartz Звичайно, це можна - дотримуючись семантику (зокрема, читання та запису) абстрактної машини. Можливо, не вдасться правильно оптимізувати - ось у чому полягає стандарт. Тепер це примітка і, таким чином, не є нормативною, і як ми обидва сказали, реалізація може визначати, що volatileробить, і дотримуватися цього. Моя думка полягає в тому, що сам код (згідно зі стандартом C ++ / абстрактна машина) не дозволяє визначити, чи gможна викликати.
Макс Лангхоф

@DavidSchwartz Код нічого подібного не показує, оскільки відсутність неявних записів не випливає з коду.
н. 'займенники' м.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.