___ ограничить поведение оптимизации функций популярных компиляторов - PullRequest
0 голосов
/ 24 февраля 2019

Рассмотрим следующую функцию:

int bar(const int* __restrict x, void g())
{
    int result = *x;
    g();
    result += *x;
    return result;
}

Нужно ли читать дважды с x из-за вызова g()?Или __restrict ион достаточно, чтобы гарантировать, что вызов g() не имеет доступа / не изменяет значение по адресу x?

При по этой ссылке мы видим наиболеепопулярные компиляторы должны сказать об этом (GodBolt; языковой стандарт C99, платформа AMD64):

  • clang 7.0: соблюдается ограничение.
  • GCC 8.3: без ограничений.
  • MSVC 19.16: без ограничений.

Правильно ли оптимизирует clang второе считывание или нет?Здесь я спрашиваю как C, так и C ++, поскольку поведение одинаковое (спасибо @PSkocik).

Связанная информация и некоторые примечания:

1 Ответ

0 голосов
/ 26 февраля 2019

Я думаю, что это фактически вопрос C, так как C - это язык, который имеет restrict с формальной спецификацией, прикрепленной к нему.

Часть стандарта C, которая регулирует использование restrict is 6.7.3.1 :

1 Позвольте D быть объявлением обычного идентификатора, который предоставляет средство для обозначения объекта P в качестве указателя с ограничением на тип T.

2 Если D появляется внутри блока и не имеет внешнего класса хранения, пусть B обозначает блок.Если D появляется в списке объявлений параметров определения функции, пусть B обозначает связанный блок.В противном случае пусть B обозначает блок main (или блок любой функции, вызываемой при запуске программы в автономной среде).

3 В дальнейшем выражение указателя E будет основано на объекте Pесли (в некоторой точке последовательности при выполнении B до оценки E) изменить P так, чтобы он указывал на копию объекта массива, на который он ранее указывал, изменит значение E.137) Обратите внимание, что «на основе»определяется только для выражений с типами указателей.

4 Во время каждого выполнения B пусть L будет любым lvalue с & L на основе P. Если L используется для доступа к значению объекта X, который он обозначает,и X также модифицируется (любым способом), тогда применяются следующие требования: T не должен быть константным.Каждое другое lvalue, используемое для доступа к значению X, должно также иметь свой адрес на основе P. Каждый доступ, который изменяет X, должен также рассматриваться как модифицирующий P, для целей этого подпункта.Если P присваивается значение выражения указателя E, которое основано на другом объекте ограниченного указателя P2, связанном с блоком B2, то либо выполнение B2 должно начинаться до выполнения B, либо выполнение B2 должно заканчиваться доназначение.Если эти требования не выполняются, то поведение не определено.

5 Здесь выполнение B означает ту часть выполнения программы, которая соответствует времени жизни объекта со скалярным типом и длительности автоматического хранения.связанный с B.

Как я понял, выполнение g() подпадает под выполнение блока bar, поэтому g() запрещено изменять *x иclang - это право на оптимизацию второй загрузки (IOW, если *x относится к неконстантному глобальному, g() не должен изменять этот глобальный).

...