Комментарии 11
Теперь то же самое ТОЛЬКО для Delphi...
Компилятор может не только код отомтимизировать, но и память.
Я в своё время сражался с llvm, пытаясь убедить его оставить в экзешнике крупный массив байт (в готовый экзешник в этот массив записывалась дополнительная информация).
gcc в такой ситуации достаточно было массив инициализировать, хотя бы не полностью - а llvm отследил варианты предсказуемой инициализации массива (например, константой отличной от нуля) и массив из экзешника выпиливал. Создавал динамически, на этапе инициализации.
Пришлось инициализировать массив сложной последовательностью, так чтобы компилятор не догадался, как её можно сгенерировать.
Экспортнуть символ в явном виде. Экспортируемые символы не могут быть удалены из результата компиляции
а llvm отследил варианты предсказуемой инициализации массива (например, константой отличной от нуля) и массив из экзешника выпиливал.
Обращение к памяти можно запретить как-либо оптимизировать. Обратиться к массиву через указатель на volatile, например. И раз компилятору запрещено для оптимизаций "заглядывать внутрь" этого обращения, ему придётся оставить предыдущие шаги - сам массив в памяти и его инициализацию.
https://godbolt.org/z/59PfdPKsh
Потом из-за нужды в стандартном способе затирания паролей в C появился memset_explicit (но не в C++). По ссылке есть другие варианты решения помимо volatile.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2897.htm#:~:text=volatile%20pointer
> (1) что, (2) почему, (3) как.
Но мой опыт объяснений "что" - говорит, что без разъяснения аритектуры компиляторов хотя бы совсем-совсем по-верхам (плюс поверхностного объяснения некоторых моментов - типа неразрешимости задачи алиасов указателей в общем случае) не приносит пользы.
Т.е. не даёт метода понять "что же произойдёт"
Рад буду, если ошибаюсь в данном случае.
Работа большая и явно потребовавшая много времени.
Для тех кто хочет копнуть глубже - цикл статей от разработчика оптимизирующих компиляторов - Поговорим об оптимизирующих компиляторах. Сказ первый: SSA-форма / Хабр
Пример с заменой умножения на сложение:
void init_scaled(int *array, int n, int scale) {
for (int i = 0; i < n; i++) {
array[i] = i * scale;
}
void init_scaled(int *array, int n, int scale) {
int tmp = 0;
for (int i = 0; i < n; i++) {
array[i] = tmp;
tmp += scale;
}
}
С одной стороны ускорили, а с другой - сделали итерации взаимозависимыми и зарубили SIMD. Как компилятор решает, что когда выгоднее?
Вычисления на этапе компиляции и бесконечный цикл.
Тут могут быть два поведения. Оба отталкиваются от рассуждения "в коде нет UB", а вот дальше варианты: "поэтому цикл надо выполнить" и "поэтому цикл можно безопасно удалить, если он не вычислился за заданное число циклов".
Оптимизация компилятора на пальцах