Oto prosty plik C z definicją enum i main
funkcją:
enum days {MON, TUE, WED, THU};
int main() {
enum days d;
d = WED;
return 0;
}
Przenosi się na następujący LLVM IR:
define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 2, i32* %2, align 4
ret i32 0
}
%2
jest ewidentnie d
zmienną, która otrzymuje 2 przypisane do niej. Co %1
odpowiada, jeśli zero jest zwracane bezpośrednio?
c
llvm
llvm-codegen
macleginn
źródło
źródło
clang-9 -S -emit-llvm simple.c
main
( godbolt.org/z/kEtS-s ). Link pokazuje, w jaki sposób zespół jest mapowany na źródłomain
, tajemnicza dodatkowa zmienna zniknie. Co ciekawe, znika również, jeślireturn
całkowicie pominiesz instrukcję (co jest legalnemain
w C i równoważne zreturn 0;
).main
jakint main(int argc, char **argv)
widziszargc
iargv
kopiujesz na stos, ale tajemnicza zmienna zerowa nadal tam jest.Odpowiedzi:
Ten
%1
rejestr został wygenerowany przez clang do obsługi wielu instrukcji return w funkcji . Wyobraź sobie, że masz funkcję obliczania silni całkowitej. Zamiast pisać w ten sposóbPrawdopodobnie byś to zrobił
Dlaczego? Ponieważ Clang wstawi
result
zmienną, która przechowuje dla ciebie wartość zwracaną. Tak To jest właśnie tego cel%1
. Spójrz na IR, aby zobaczyć nieco zmodyfikowaną wersję twojego kodu.Zmodyfikowany kod,
IR,
Teraz widzisz, że
%1
robi się przydatny, co? Jak zauważyli inni, dla funkcji z tylko jedną instrukcją return ta zmienna prawdopodobnie zostanie usunięta przez jedno z przejść optymalnych lvvm.źródło
Dlaczego to ma znaczenie - jaki jest faktyczny problem?
Myślę, że głębsza odpowiedź może być następująca: architektura LLVM opiera się na dość prostych interfejsach i wielu przejściach. Interfejsy muszą generować poprawny kod, ale nie musi to być dobry kod. Mogą zrobić najprostszą rzecz, która działa.
W tym przypadku Clang generuje kilka instrukcji, które okazują się nie być wykorzystywane do niczego. Zasadniczo nie stanowi to problemu, ponieważ część LLVM pozbywa się zbędnych instrukcji. Clang ufa, że tak się stanie. Clang nie musi unikać emitowania martwego kodu; jego wdrożenie może koncentrować się na poprawności, prostocie, testowalności itp.
źródło
Ponieważ Clang jest wykonywany z analizą składni, ale LLVM nie zaczął nawet od optymalizacji.
Interfejs Clanga wygenerował IR (reprezentacja pośrednia), a nie kod maszynowy. Te zmienne są SSA (pojedyncze przypisania statyczne); nie były jeszcze związane z rejestrami, a właściwie po optymalizacji, nigdy nie będą, ponieważ są zbędne.
Ten kod jest dosłownie reprezentacją źródła. To właśnie przylega do LLVM w celu optymalizacji. Zasadniczo LLVM zaczyna od tego i optymalizuje od tego momentu. Rzeczywiście, dla wersji 10 i x86_64, llc -O2 ostatecznie wygeneruje:
źródło