Почему умножение на постоянные целые дроби со знаком не оптимизированы? - PullRequest
14 голосов
/ 20 июня 2020

Поскольку знаковое целочисленное переполнение является неопределенным поведением, я ожидал, что три функции ниже будут скомпилированы в ту же или похожую сборку. Однако это не так. test2 немного отличается от test1, а test3 использует две инструкции imul, которые не требуются для других примеров.

int test1(int x)
{
    return x * 5 / 2;
}

int test2(int x)
{
    return x * 10 / 4;
}

int test3(int x)
{
    return x * 50 / 20;
}

сравнение в проводнике компилятора

Есть ли причина, по которой компиляторы не выполняют такую ​​оптимизацию?

1 Ответ

1 голос
/ 06 августа 2020

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

  1. Если реализация гарантирует, что целочисленные сложения и умножения всегда будут вести себя так, как если бы они были выполнены со значениями, достаточно большими, чтобы удерживать результат, а затем два дополнения, усеченных до размера типа после каждой операции, замена x*(m*a)/(m*b) на x*a/b нарушит такие гарантии.
  2. Если реализация гарантирует, что целочисленное сложение и умножение всегда будет вести себя так, как если бы они давали какое-то число, но не гарантирует, что временные значения будут усечены до любого конкретного размера, такая оптимизация будет допустимой.
  3. Если реализация ничего не гарантирует о последствиях переполнения, и требует, чтобы программисты избегали их любой ценой, потому что они могут вызывать ошибочное поведение даже в тех частях программы, которые не используют результат, такая оптимизация будет b e действителен.

Компилятор g cc предлагает варианты, чтобы либо поддержать вариант сильной гарантии № 1, либо воздержаться от предоставления каких-либо гарантий, а также возможность целочисленного переполнения, нарушающего части кода, которые не используйте результат не только теоретический. Поскольку вариант №3 был бы безрассудно опасен для программ, которые будут получать ввод из потенциально ненадежных источников, и поскольку g cc не предлагает никаких настроек между №1 и №3, многие программы, включая те, где вариант №2 соответствовал бы требования, создаются с помощью флага fwrapv, который принудительно устанавливает вариант №1.

...