Древний подход от Davidson&Fraser как раз ведь и основан на peephole-оптимизации.
Вот пример из проекта MIR:
https://github.com/vnmakarov/mir/blob/master/mir-gen.c#L5082Здесь загрузка константы отдельной командой заменяется в add значением в поле disp (смещение).
К слову сказать, сам термин peephole-оптимизация сегодня довольно неоднозначный. Под ним часто подразумевают любые локальные замены. Хоть на DAG, хоть в списке команд. Хоть до распределения регистров, хоть после него.
Не очень ясно, почему в MIR не используется выбор команд на тех же деревьях. Возможно, использование динамического программирования представляется слишком накладным, но ведь вместо учета стоимостей можно использовать классический maximal munch (Cattell).
На практике выбор команд с DAG не является NP-полным. Мы можем за линейное время проверить два корневых (rooted) DAG с пронумерованными ребрами. В LLVM как раз используется жадный подход.
Построение SSA для линейного участка, то есть переименование без использования phi-функций, это одно из старейших оптимизирующих преобразований. Здесь, кстати говоря, есть давнее, но занятное обсуждение "Block Single Assignment" :)
https://compilers.iecc.com/comparch/article/07-04-075