Dlaczego pośrednia reprezentacja LLVM (LLVM IR) jest bardziej złożona niż drzewiasta?
Alternatywnie, dlaczego implementacje językowe są ukierunkowane na LLVM IR, a nie na AST?
Nie próbuję zadawać jednocześnie dwóch różnych pytań, jeśli tak to wygląda. Wydaje mi się, że zarówno programiści, jak i programiści biblioteki doszli do konsensusu, że API LLVM, nic więcej i nic innego, jest oczywiście dobrym projektem oprogramowania, a moje pytanie brzmi „dlaczego?”.
Powodem, dla którego pytam, jest to, że LLVM może zapewnić więcej funkcji dla frontendów, jeśli IR jest podobny do AST, ponieważ wtedy narzędzia oparte na AST clanga mogłyby być użyte do dowolnego frontendu. Alternatywnie, języki kierowane na LLVM IR mogą uzyskać większą funkcjonalność, jeśli będą atakować AST.
Clang ma klasy i funkcje do tworzenia i pracy z ASTami i jest to jedyny projekt frontendowy, który jest silnie powiązany z projektem LLVM, więc dlaczego funkcja AST clanga jest niezależna od LLVM?
Z mojej głowy wiem, że Rust (rustc), D (ldc) i Haskell (GHC) mogą używać LLVM jako backendu, ale nie używają Clang AST (o ile wiem, mógłbym mylić się). Nie znam wszystkich wewnętrznych szczegółów tych kompilatorów, ale przynajmniej Rust i D z pewnością wydają się być kompatybilne z AST. Może Haskell też, ale nie jestem tego pewien.
Czy dzieje się tak z przyczyn historycznych (LLVM początkowo był „maszyną wirtualną niskiego poziomu”, a później pojawił się w pobliżu)? Czy to dlatego, że inne nakładki chcą mieć jak największą kontrolę nad tym, co zasilają LLVM? Czy istnieją fundamentalne powody, dla których AST klangu jest nieodpowiednia dla języków „nie podobnych do C”?
Nie zamierzam, aby to pytanie było ćwiczeniem w czytaniu w myślach. Chcę tylko, aby była pomocna dla tych z nas, którzy są ciekawi, ale jeszcze nie biegle, projektowania kompilatora. Ponieważ projekty LLVM i clang są opracowywane publicznie, mam nadzieję, że ktoś zaznajomiony z rozwojem tych projektów może odpowiedzieć lub że odpowiedź jest wystarczająco oczywista dla niektórych kompilujących nerdów, na których czują się wystarczająco pewni, aby odpowiedzieć.
Aby uprzedzić niektóre oczywiste, ale niezadowalające odpowiedzi:
Tak, posiadanie IR przypominającego asembler daje większą kontrolę każdemu, kto tworzy IR (być może X lang ma lepszą bazę kodu i format AST niż clang), ale jeśli to jedyna odpowiedź, to pytanie brzmi „dlaczego LLVM ma tylko asembler - jak IR zamiast wysokiego poziomu drzewiastego IR i niskiego poziomu montażowego IR? ".
Tak, parsowanie języka programowania na AST nie jest trudne (przynajmniej w porównaniu z innymi etapami kompilacji). Mimo to, po co używać osobnych AST? Jeśli nic innego, korzystanie z tego samego AST pozwala na korzystanie z narzędzi działających na AST (nawet prostych rzeczy, takich jak drukarki AST).
Tak, zdecydowanie się zgadzam, że bycie bardziej modułowym to dobra rzecz, ale jeśli to jedyny powód, to dlaczego inne implementacje językowe mają tendencję do celowania w LLVM IR zamiast AST clanga?
Te uprzedzenia mogą być błędne lub przeoczyć szczegóły, więc nie krępuj się udzielić tych odpowiedzi, jeśli masz więcej szczegółów lub moje założenia są błędne.
Dla każdego, kto chce odpowiedzieć na bardziej ostateczne pytanie: jakie są zalety i wady podczerwieni podobnej do montażu w porównaniu do podczerwieni drzewiastej?
źródło
Odpowiedzi:
Jest tu kilka powiązanych ze sobą pytań, postaram się je jak najlepiej rozdzielić.
Dlaczego inne języki bazują na LLVM IR i nie zakłócają AST?
Jest tak po prostu dlatego, że clang jest frontonem C / C ++, a wytwarzany przez niego AST jest ściśle powiązany z C / C ++. Przydałby się inny język, ale wymagałby niemal identycznej semantyki z jakimś podzbiorem C / C ++, co jest bardzo ograniczające. Jak zauważyłeś, parsowanie do AST jest dość proste, więc ograniczenie wyborów semantycznych raczej nie będzie warte małej oszczędności.
Jeśli jednak piszesz narzędzia do C / C ++, np. Analizatory statyczne, to ponowne użycie AST ma sens, ponieważ o wiele łatwiej jest pracować z AST niż surowy tekst, jeśli pracujesz z C / C ++ .
Dlaczego LLVM IR ma taką postać?
LLVM IR wybrano jako odpowiednią formę do pisania optymalizacji kompilatora. W związku z tym jego podstawową cechą jest to, że ma postać SSA . Jest to dość niski poziom podczerwieni, dzięki czemu można go stosować w wielu różnych językach, np. Nie zapisuje pamięci, ponieważ jest bardzo różny w różnych językach.
Obecnie zdarza się, że pisanie optymalizacji kompilatora jest dość specjalistycznym zadaniem i często jest prostopadłe do projektowania funkcji językowych. Jednak szybkie uruchamianie skompilowanego języka jest dość ogólnym wymogiem. Ponadto konwersja z LLVM IR na ASM jest dość mechaniczna i ogólnie nie interesująca dla projektantów języków.
Dlatego obniżenie języka do LLVM IR daje projektantowi języka wiele „darmowych rzeczy”, które są bardzo przydatne w praktyce, pozostawiając go do skoncentrowania się na samym języku.
Czy przydałaby się inna podczerwień (OK, nie pytano, ale jakby sugerowano)?
Absolutnie! AST są całkiem dobre dla niektórych przekształceń w strukturze programu, ale są bardzo trudne w użyciu, jeśli chcesz przekształcić przebieg programu. Formularz SSA jest ogólnie lepszy. Jednak LLVM IR ma bardzo niski poziom, więc część struktury wysokiego poziomu zostaje utracona (celowo, więc ma bardziej ogólne zastosowanie). Korzystne może być posiadanie IR między AST a IR na niskim poziomie. Zarówno Rust, jak i Swift przyjmują to podejście i mają między nimi wysoki poziom IR.
źródło