алсо, вспомнил вычитаную идею про то, что делать с указателями в SSA. если кто-то не помнит, то напомню, что SSA жёстко заточен под иммутабельность, и очень плохо переносит store/load. то бишь, работу с переменными через получение указателя и потом load/store. основная идея SSA такая, что локалам мы назначаем псевдорегистры, и не генерируем обращения к памяти в IR. эти обращения появляются только на фазе деструкции, когда создаём машинный код.
такой идеальный расклад, конечно, удобен для теоретических исследований, но… на практике у нас всегда есть указатели — как минимум на объекты в хипе. и даже в обероне бывают и другие — хотя бы в виде `SYSTEM.ADR()` с последующими `PUT` и `GET`. которые желательно компилировать по месту, а не вызовами интринсиков.
так вот. есть интересная идея на предмет того, что делать с адресами в SSA: надо просто сделать `store` не void-ом, а возвращать оттуда новый указатель. это решение не идеально, конечно, но значительно упрощает анализ data flow.
Код:
v0 = imm 4
v1 = load p
v2 = add v0, v1
p0 = store p, v2
...
v4 = load p0
...
p1 = store p0, v2
…
vN = load p1
каждый адрес используется в store ровно один раз. таким образом мы отслеживаем мутации памяти, и можем легко убирать лишние load и store обычными алгоритмами. очевидно, например, что `load p0` можно заменить на `v2`, если `p0` строго доминирует над соответствующим `load`. и `store`, результат которого нигде не используется, можно смело выкинуть (проверив, что это не для глобала или хипа).
я, собственно, сейчас и делаю ровно то, что выше описано — но без автоматических идентификаторов, и поэтому приходится сканировать LIR. и делаю на лету, когда CFG ещё нет, так что вместо проверки на строгое доминирование останавливаюсь просто на потенциальной границе базового блока.
p.s.: вопрос алиасинга я умышленно упустил: это отдельная тема.