2016-04-24 9 views
5

Obecnie uczę się języków programowania złożonego i języka C. Mam kilka pytań na ten temat.Montaż w porównaniu z kodem C

kod C

int arith(int x, int y, int z) { 
    int t1 = x + y; 
    int t2 = z*48; 
    int t3 = t1 & 0xFFFF; 
    int t4 = t2 * t3; 
    return t4; 
} 

kod Zgromadzenie

movl 16(%ebp),%eax   z 
leal (%eax,%eax,2), %eax z*3 
sall $4,%eax    t2 = z*48 
movl 12(%ebp),%edx   y 
addl 8(%ebp),%edx   t1 = x+y 
andl $65535,%edx   t3 = t1&0xFFFF 
imull %edx,%eax    Return t4 = t2*t3 

Zamiast Leal, a następnie przesunięcie o 4 pomnożyć oo przez 48, może po prostu użyć imull $ 48,% eax?

Jest to również kilkakrotne użycie rejestru% edx. Czy to oznacza, że ​​t1 jest nadpisywane? Innymi słowy, czy nadal będę w stanie odzyskać t1 tuż przed t4, jeśli chcę?

+1

Tak, Nie (Zmienna "t1" jest zoptymalizowana z dala) i Nie. Dla ostatniego pytania 'x + y' jest obliczane, ale nigdy nie zapisane. _EDX_ miał wartość 'x + y' po' addl 8 (% ebp),% edx', ale instrukcja 'andl 65535,% edx' ją niszczy. Jeśli przeniesiesz _EDX_ do rejestru takiego jak _ECX_ po 'addl 8 (% ebp),% edx', to nadal będziesz miał dostęp do części x + y obliczeń. –

+0

Cool. Czy kod C byłby naprawdę przetłumaczony za kulisami w taki sposób, aby nie zapisywał każdej zmiennej we własnym rejestrze? – Dylan

+2

Nie, jeśli nie jest to konieczne. Taka jest moc kompilatora optymalizującego. – usr2564301

Odpowiedz

2

Próba dopasowania zestawu do linii kodu po linii prawdopodobnie nie jest najlepszym sposobem podejścia do tego. Kompilator dokonuje kilku optymalizacji, aby twój program działał tak wydajnie, jak to tylko możliwe, dlatego możesz zauważyć pewne niespójności między kodem.

Aby odpowiedzieć na twoje pierwsze pytanie, technicznie to zadziała, ale po raz kolejny kompilator dokona kilku optymalizacji. Więc chociaż może wydawać się bardziej intuicyjne w użyciu imul, kompilator ustalił, że leal i sall są bardziej wydajne. EDYCJA: Po prostu chcę zauważyć, że operatory przesunięcia bitowego są prawie zawsze używane zamiast imul, jeśli to możliwe. Przesunięcie bitów jest znacznie tańsze dla procesora, ponieważ całkiem dosłownie przesuwa wartości bitowe, zamiast próbować wykonać pewne matematyczne operacje, które mogą zająć więcej czasu procesora.

Teraz w odniesieniu do "nadpisywania" t1. Zgromadzenie nie ma żadnych informacji o zmiennych programu - wszystko, co wie, to że musi wykonać pewne operacje na niektórych wartościach. Podczas gdy zespół mógłby potencjalnie używać 4 różnych rejestrów do przechowywania t1-4, kompilator ustalił, że jest to niepotrzebne i że potrzebujesz tylko 2 rejestrów dla wszystkich wartości. Jeśli o tym pomyślisz, powinno to mieć sens. Twoja funkcja może zostać zredukowana do zaledwie kilku linii kodu. Oczywiście nie jest to dobry pomysł, ponieważ uniemożliwiłoby to odczytanie, ale montaż niekoniecznie musi być "czytelny". Jeśli powróciłeś do swojego programu i wykonałeś jakąś inną operację z t1 przed powrotem t4, możesz zauważyć, że twój zestaw jest inny niż wcześniej i że może on używać innego rejestru, w zależności od tego, w jaki sposób używana jest wartość.

Jeśli naprawdę potrzebujesz wersji programu barebones w zestawie, skompiluj z flagą -Og, aby wyłączyć optymalizacje kompilatora. Może nadal nie pasuje do twojego kodu, ale może ci ułatwić zrozumienie tego, co się dzieje.

+1

Dziękuję. Wszystko, co powiedziałeś, miało dla mnie sens. To fajne, jak optymalizuje się za kulisami. – Dylan

+1

Dobre źródło informacji o optymalizacji instrukcji można znaleźć w tym dokumencie [Agner Fog] (http: //www.agner.org/optimize/instruction_tables.pdf). W zależności od architektury LEAL może nie dotrzeć nawet do ALU. Na niektórych architekturach x86 odbywa się to jako część AGU. –