Patrząc na wygenerowany przez ICC 17 kod do iterowania na std :: unordered_map <> (używając https://godbolt.org), byłem bardzo zdezorientowany.Dlaczego ICC rozwija tę pętlę w ten sposób i używa lea do arytmetyki?
I destylowanej dół przykład tak:
long count(void** x)
{
long i = 0;
while (*x)
{
++i;
x = (void**)*x;
}
return i;
}
przygotowanie tego z ICC 17, z flagą -O3 prowadzi do następującego demontażu:
count(void**):
xor eax, eax #6.10
mov rcx, QWORD PTR [rdi] #7.11
test rcx, rcx #7.11
je ..B1.6 # Prob 1% #7.11
mov rdx, rax #7.3
..B1.3: # Preds ..B1.4 ..B1.2
inc rdx #7.3
mov rcx, QWORD PTR [rcx] #7.11
lea rsi, QWORD PTR [rdx+rdx] #9.7
lea rax, QWORD PTR [-1+rdx*2] #9.7
test rcx, rcx #7.11
je ..B1.6 # Prob 18% #7.11
mov rcx, QWORD PTR [rcx] #7.11
mov rax, rsi #9.7
test rcx, rcx #7.11
jne ..B1.3 # Prob 82% #7.11
..B1.6: # Preds ..B1.3 ..B1.4 ..B1.1
ret #12.10
porównaniu z oczywistą realizacji (które używają gcc i clang, nawet dla -O3), wydaje się robić kilka rzeczy inaczej:
- Rozwija pętlę, z dwoma dekresami przed powrotem do pętli - jednak w środku tego jest skok warunkowy.
- wykorzystuje lea dla niektórych operacji arytmetycznych
- To zachowuje licznik (Inc RDX) dla każdego dwa iteracji pętli while natychmiast oblicza odpowiednich liczników każdej iteracji (w Rax i RSI)
Jakie są potencjalne korzyści z robienia tego wszystkiego? Zakładam, że może to mieć coś wspólnego z planowaniem?
Dla porównania, jest to kod generowany przez gcc 6.2:
count(void**):
mov rdx, QWORD PTR [rdi]
xor eax, eax
test rdx, rdx
je .L4
.L3:
mov rdx, QWORD PTR [rdx]
add rax, 1
test rdx, rdx
jne .L3
rep ret
.L4:
rep ret
Zalety 'lea' obejmują: (1) Umożliwia dwa operandy źródłowe, z których oba mogą się różnić od wyniku, podczas gdy' add' wymaga, aby jeden operand źródłowy był identyczny z wynikiem; użycie 'lea' może uniknąć użycia dodatkowego' mov' aby zachować współdzielony argument źródłowy (2) Pozwala na proste mnożenie za pomocą wbudowanego współczynnika skalowania (3) Nie wpływa na flagi, pozwalając na większą elastyczność w planowanie instrukcji. – njuffa
'lea' został użyty do obliczeń arytmetycznych od początku czasów. Zasadniczo jest to bardziej skomplikowane niż 'inc' /' dec' i 'lea' może to zrobić, wtedy' lea' jest najbardziej wydajnym sposobem na zrobienie tego. Z tego powodu nie jest jasne, co skłoniło Pana do pytania o "lea". Jeśli potrafisz czytać zespół, powinieneś już wiedzieć o "lea" i jego roli. – AnT