Для 1, название оптимизации, которую вы ищете, это общее исключение подвыражений (CSE). В зависимости от вашего представления это может быть довольно легко. Обычно компилятор будет иметь некоторое промежуточное представление программы, где операции разбиты в максимально возможной степени и линеаризуются. Так, например, выражение c = a * b + a * b
может быть разбито на:
v1 = a * b
v2 = a * b
c = v1 + v2
Таким образом, вы можете выполнять CSE на очень низком уровне, ища операции с одним и тем же оператором и операндами. При обнаружении дубликата (в данном случае v2) вы заменяете все его экземпляры оригиналом. Таким образом, мы могли бы упростить приведенный выше код:
v1 = a * b
c = v1 + v1
Обычно это предполагает, что вы назначаете каждую переменную только один раз (одна статическая форма назначения), но вы можете реализовать что-то подобное без этого ограничения. Это становится более сложным, когда вы пытаетесь выполнить эту оптимизацию в разных филиалах. Как упоминает Зифре, посмотрите на устранение частичной избыточности.
В любом случае, вы получаете некоторые базовые улучшения, и все, что вам нужно отслеживать, это базовые выражения. Возможно, вы захотите пойти дальше и искать арифметические тождества. Например, a * b
совпадает с b * a
. Также x * (y + z) = x * y + x * z
. Это усложняет вашу оптимизацию, и не ясно, что это даст вам значительное улучшение производительности. Как ни странно, большая часть преимуществ оптимизации CSE обеспечивается вычислениями адресов, такими как доступ к массиву, и вам не понадобятся сложные идентификаторы, подобные приведенным выше.
Для 2, какое снижение прочности полезно, действительно зависит от архитектуры, для которой вы компилируете. Обычно это включает в себя преобразование умножения и деления в сдвиги, сложения и вычитания.