Skutecznie eliminuj typowe podwyrażenia w drzewie wyrażeń .NET

Napisałem DSL i kompilator, który generuje z niego drzewo wyrażeń .NET. Wszystkie wyrażenia w drzewie są wolne od efektów ubocznych, a wyrażenie jest gwarantowane jako wyrażenie „bez instrukcji” (brak lokalnych, pętli, bloków itp.). (Edytować: Drzewo może zawierać literały, dostęp do właściwości, standardowe operatory i wywołania funkcji - które mogą robić wymyślne rzeczy, takie jak zapamiętywanie wewnątrz, ale są zewnętrznie wolne od efektów ubocznych).

Teraz chciałbym przeprowadzić na nim optymalizację „Common sub-expression elimination”.

Na przykład, podając drzewo odpowiadające lambdzie C #:

foo =>      (foo.Bar * 5 + foo.Baz * 2 > 7) 
         || (foo.Bar * 5 + foo.Baz * 2 < 3)  
         || (foo.Bar * 5 + 3 == foo.Xyz)

... Chciałbym wygenerować odpowiednik drzewa (zignoruj ​​fakt, że niektóre semantyki zwierania są ignorowane):

foo =>
{
     var local1 = foo.Bar * 5;

     // Notice that this local depends on the first one.        
     var local2 = local1 + foo.Baz * 2; 

     // Notice that no unnecessary locals have been generated.
     return local2 > 7 || local2 < 3 || (local1 + 3 == foo.Xyz);
}

Jestem zaznajomiony z pisaniem wyrażeń-odwiedzających, ale algorytm tej optymalizacji nie jest dla mnie od razu oczywisty - mógłbym oczywiście znaleźć „duplikaty” w drzewie, ale oczywiście istnieje pewna sztuczka do analizy zależności wewnątrz i między drzewa, aby skutecznie i poprawnie eliminować podwyrażenia.

Szukałem algorytmów w Google, ale ich implementacja wydaje się dość skomplikowana. Ponadto wydają się bardzo „ogólne” i niekoniecznie biorą pod uwagę prostotę drzew, które mam pod uwagę.

questionAnswers(5)

yourAnswerToTheQuestion