Wyobrażam sobie, że wielu implementatorów kompilatorów dla typowych języków imperatywnych po prostu nie znało technik kompilacji CPS i opartych na CPS. W społeczności programowania funkcjonalnego zarówno CPS, jak i kompilacja oparta na CPS są bardzo dobrze znanymi technikami - ta ostatnia pochodzi z pracy Guy Steele. Niemniej jednak, nawet w społeczności FP, większość kompilatorów nie używa technik kompilacji opartych na CPS, chyba że sam język obsługuje takie operatory sterowania call/cc
. Stosuje się coś bardziej podobnego do normalnej formy administracyjnej (ANF) (czasami nazywanej również normalną formą monadyczną, która jest ściśle powiązana z przyczyn, które staną się jasne), która ma jeszcze ściślejszy związek z SSA niż CPS .
O ile dobrze pamiętam, normalna forma administracyjna bierze swoją nazwę od faktu, że kompilacja oparta na CPS może prowadzić do beta-redeksów w kodzie pośrednim, które nie odpowiadają żadnej części w kodzie źródłowym. Były one określane jako „redeksy administracyjne”. Mogły one ulec skróceniu w czasie kompilacji, ale przeprowadzono wiele badań nad przeprowadzeniem transformacji CPS, która wygenerowałaby kod bez administracyjnych powtórzeń. Następnie celem było uzyskanie wyników w normalnej formie, w której wszystkie „administracyjne” redeksy zostały zredukowane, i to było początkiem normalnej formy administracyjnej. Nie trzeba było długo czekać, aby zrozumieć, że nie było wiele korzyści z postrzegania tego jako (n optymalizacja) dwuetapowego procesu: transformacja CPS, redukcja redeksów administracyjnych. W szczególności, normalna forma administracyjna wygląda raczej jak styl monadyczny, jak praktykowana (ręcznie), szczególnie w Haskell. Transformację CPS można rozumieć jako konwersję do stylu monadycznego, w którym akurat używasz monady CPS (więc istnieje naprawdę wiele sposobów na „konwersję” do stylu monadycznego odpowiadających różnym porządkom oceny). Ogólnie rzecz biorąc, możesz używać zupełnie innej monady, więc konwersja do stylu monadycznego, a zatem normalna forma administracyjna, nie jest tak naprawdę szczególnie związana z CPS.
Niemniej jednak CPS w porównaniu z ANF miał pewne zalety. W szczególności istniały pewne optymalizacje, które można wykonać w CPS za pomocą standardowych optymalizacji, takich jak redukcja wersji beta, które wymagały (pozornie) reguł ad-hoc dla ANF. Z perspektywy monadycznej reguły te odpowiadają konwersjom dojazdów do pracy. Rezultatem jest teraz teoria, która może wyjaśnić, jakie reguły należy dodać i dlaczego. Ostatni papier daje (nowy) i dość dokładny opis tego (z logicznego punktu widzenia) i jego pokrewne sekcja praca służy jako krótki, ale godnej badania i odniesienia do literatury na tematy wspominam.
Problem z CPS wiąże się z jedną z jego głównych zalet. Transformacja CPS pozwala na implementację takich operatorów sterowania call/cc
, ale oznacza to, że każde nielokalne wywołanie funkcji w kodzie pośrednim CPS musi być traktowane jako potencjalnie wykonujące efekty sterowania. Jeśli twój język zawiera operatory sterowania, to jest tak, jak powinno być (chociaż nawet wtedy większość funkcji prawdopodobnie nie wykonuje żadnych kontroli shenanigans). Jeśli twój język nie obejmuje operatorów sterujących, istnieją globalne niezmienniki dotyczące użycia kontynuacji, które nie są widoczne lokalnie. Oznacza to, że istnieją optymalizacje, których nie można wykonać na ogólnym kodzie CPS, których wykonanie przy szczególnie dobrze zachowanym użyciu CPS byłoby rozsądne. Jednym ze sposobów jest tozmiana precyzji analiz przepływu danych i kontroli . (Transformacja CPS pomaga w pewien sposób, boli w innych, chociaż sposoby, w które pomaga, są głównie spowodowane duplikacją, a nie samym aspektem CPS.) 1 Możesz oczywiście dodać reguły i dostosować analizy, aby to zrekompensować (tj. Wykorzystać globalne niezmienniki), ale częściowo pokonałeś jedną z głównych zalet kompilacji opartej na CPS, a mianowicie to, że wiele (pozornie) specjalnych optymalizacji ad-hoc staje się specjalnymi przypadkami optymalizacji ogólnego przeznaczenia (szczególnie redukcji beta ).
Ostatecznie, chyba że twój język ma operatorów sterujących, zwykle nie ma zbyt wiele powodów, aby używać schematu kompilacji opartego na CPS. Po zrekompensowaniu problemów, o których wspomniałem powyżej, zazwyczaj eliminujesz zalety kompilacji opartej na CPS i tworzysz coś równoważnego z nieużywaniem CPS. W tym momencie CPS tworzy zawiły wygląd kodu pośredniego, który nie przynosi większych korzyści. Argument za kompilacją opartą na CPS z 2007 roku rozwiązuje niektóre z tych problemów i przedstawia inne korzyści przy użyciu innej formy konwersji CPS. To, co porusza papier, jest częściowo ujęte w artykule (2017) , o którym wspominałem wcześniej.
1 Czy równoważność między SSA i CPS nie czyni tego mniej lub bardziej niemożliwym? Nie. Jedną z pierwszych rzeczy, która mówi o wprowadzeniu tego równoważnika , jest to, że równoważność nie działa dla dowolnego kodu CPS, ale działa na wyjściu transformacji CPS (którą definiują) dla języka bez operatorów sterujących.