#c #msvcrt #loop-unrolling
#c #msvcrt #развертывание цикла
Вопрос:
У меня возникли проблемы с пониманием того, как компилятор MSVC разворачивает следующий цикл (извините за мое плохое понимание языка ассемблера):
#define NUM_ITERATIONS (1000 * 1000 * 1000)
double dummySum = 0;
for (int x = 0; x < NUM_ITERATIONS; x ) {
if (x amp; 1)
dummySum = x;
}
Это сгенерированная сборка:
00007FF7B4511070 xorps xmm1,xmm1
double dummySum = 0;
00007FF7B4511073 mov ecx,2
00007FF7B4511078 nop dword ptr [rax rax]
if (x amp; 1)
00007FF7B4511080 lea eax,[rcx-2]
00007FF7B4511083 mov r8d,eax
00007FF7B4511086 and r8d,1
00007FF7B451108A je someTest 28h (07FF7B4511098h)
dummySum = x;
00007FF7B451108C movd xmm0,eax
00007FF7B4511090 cvtdq2pd xmm0,xmm0
00007FF7B4511094 addsd xmm1,xmm0
if (x amp; 1)
00007FF7B4511098 lea edx,[rcx-1]
00007FF7B451109B and edx,1
00007FF7B451109E je someTest 3Fh (07FF7B45110AFh)
dummySum = x;
00007FF7B45110A0 lea eax,[rcx-1]
00007FF7B45110A3 movd xmm0,eax
00007FF7B45110A7 cvtdq2pd xmm0,xmm0
00007FF7B45110AB addsd xmm1,xmm0
00007FF7B45110AF test r8d,r8d
if (x amp; 1)
00007FF7B45110B2 je someTest 50h (07FF7B45110C0h)
dummySum = x;
00007FF7B45110B4 movd xmm0,ecx
00007FF7B45110B8 cvtdq2pd xmm0,xmm0
00007FF7B45110BC addsd xmm1,xmm0
00007FF7B45110C0 test edx,edx
if (x amp; 1)
00007FF7B45110C2 je someTest 63h (07FF7B45110D3h)
dummySum = x;
00007FF7B45110C4 lea eax,[rcx 1]
00007FF7B45110C7 movd xmm0,eax
00007FF7B45110CB cvtdq2pd xmm0,xmm0
00007FF7B45110CF addsd xmm1,xmm0
00007FF7B45110D3 test r8d,r8d
if (x amp; 1)
00007FF7B45110D6 je someTest 77h (07FF7B45110E7h)
dummySum = x;
00007FF7B45110D8 lea eax,[rcx 2]
00007FF7B45110DB movd xmm0,eax
00007FF7B45110DF cvtdq2pd xmm0,xmm0
00007FF7B45110E3 addsd xmm1,xmm0
00007FF7B45110E7 test edx,edx
if (x amp; 1)
00007FF7B45110E9 je someTest 8Ah (07FF7B45110FAh)
dummySum = x;
00007FF7B45110EB lea eax,[rcx 3]
00007FF7B45110EE movd xmm0,eax
00007FF7B45110F2 cvtdq2pd xmm0,xmm0
00007FF7B45110F6 addsd xmm1,xmm0
00007FF7B45110FA test r8d,r8d
if (x amp; 1)
00007FF7B45110FD je someTest 9Eh (07FF7B451110Eh)
dummySum = x;
00007FF7B45110FF lea eax,[rcx 4]
00007FF7B4511102 movd xmm0,eax
00007FF7B4511106 cvtdq2pd xmm0,xmm0
00007FF7B451110A addsd xmm1,xmm0
00007FF7B451110E test edx,edx
if (x amp; 1)
00007FF7B4511110 je someTest 0B1h (07FF7B4511121h)
dummySum = x;
00007FF7B4511112 lea eax,[rcx 5]
00007FF7B4511115 movd xmm0,eax
00007FF7B4511119 cvtdq2pd xmm0,xmm0
00007FF7B451111D addsd xmm1,xmm0
00007FF7B4511121 test r8d,r8d
if (x amp; 1)
00007FF7B4511124 je someTest 0C5h (07FF7B4511135h)
dummySum = x;
00007FF7B4511126 lea eax,[rcx 6]
00007FF7B4511129 movd xmm0,eax
00007FF7B451112D cvtdq2pd xmm0,xmm0
00007FF7B4511131 addsd xmm1,xmm0
00007FF7B4511135 test edx,edx
if (x amp; 1)
00007FF7B4511137 je someTest 0D8h (07FF7B4511148h)
dummySum = x;
00007FF7B4511139 lea eax,[rcx 7]
00007FF7B451113C movd xmm0,eax
00007FF7B4511140 cvtdq2pd xmm0,xmm0
00007FF7B4511144 addsd xmm1,xmm0
for (int x = 0; x < NUM_ITERATIONS; x ) {
00007FF7B4511148 add ecx,0Ah
00007FF7B451114B lea eax,[rcx-2]
00007FF7B451114E cmp eax,3B9ACA00h
00007FF7B4511153 jl someTest 10h (07FF7B4511080h)
}
Я понимаю эту часть (начало цикла):
// if (x % 2 == 0) jump over the sumation
00007FF7B4511073 mov ecx,2 // ecx/rcx = 2
00007FF7B4511080 lea eax,[rcx-2] // eax = rcx - 2
00007FF7B4511083 mov r8d,eax // r8d = eax
00007FF7B4511086 and r8d,1 // r8x amp; 1
00007FF7B451108A je someTest 28h (07FF7B4511098h) // jump if zero
// add double
00007FF7B451108C movd xmm0,eax
00007FF7B4511090 cvtdq2pd xmm0,xmm0
00007FF7B4511094 addsd xmm1,xmm0
Но я не понимаю, как последующие инструкции перехода, похоже, пропускают следующую lea
инструкцию, если я смотрю на адреса (предполагается, что происходит переход) — обратите внимание, что я пропустил инструкции между переходами из приведенного выше списка:
00007FF7B45110C0 test edx,edx
00007FF7B45110C2 je someTest 63h (07FF7B45110D3h)
... addresses in between omitted ...
00007FF7B45110D3 test r8d,r8d
00007FF7B45110D6 je someTest 77h (07FF7B45110E7h)
... addresses in between omitted ...
00007FF7B45110E7 test edx,edx
00007FF7B45110E9 je someTest 8Ah (07FF7B45110FAh)
... addresses in between omitted ...
00007FF7B45110FA test r8d,r8d
00007FF7B45110FD je someTest 9Eh (07FF7B451110Eh)
... addresses in between omitted ...
00007FF7B451110E test edx,edx
00007FF7B4511110 je someTest 0B1h (07FF7B4511121h)
Если выполняется каждый переход, кажется, что это будет просто чередование test r8d,r8d
test edx,edx
инструкций и, без загрузки следующего значения.
Что я здесь неправильно интерпретирую?
Комментарии:
1. Последняя часть (
test edx,edx
материал) отсутствует в списке. Здесь чего-то не хватает.2. @MichaelWalz: не могли бы вы уточнить? Какая именно адресная строка? Я проверил адреса, и они, похоже, совпадают (если я не ошибся). Т.Е. Если вы начинаете с адреса
00007FF7B45110C0
в списке и выполняетеje
переходы.3. Хорошо, я понимаю, вам следует опубликовать раздел «прыжки через эту часть …», который вы удалили, это, безусловно, важно.
4. @MichaelWalz: это в оригинальном списке, это была просто попытка показать (если я не ошибаюсь), что эти инструкции, похоже, пропускаются, если выполняется переход.
5. Вы видите эти
lea eax,[rcx X]
инструкции? Вы видите, насколькоX
в тех инструкциях все по-другому? Это может быть как-то связано с этим. Вы также должны запустить отладчик и пошагово просмотреть ассемблерный код (не исходный код).
Ответ №1:
Хорошо, понял, я прошел через дизассемблирование шаг за шагом; компилятор довольно умный. Цикл развертывается для выполнения 10 раз за итерацию, и эти инструкции расположены так, что r8d
и edx
загружаются только один раз за итерацию:
lea eax,[rcx-2]
mov r8d,eax
and r8d,1 // r8d is 0 here
...
lea edx,[rcx-1]
and edx,1 // edx is 1 here
После этого эти регистры больше не загружаются для остальной части итерации, потому что компилятор, очевидно, понял, что amp; 1
на каждом нечетном шаге вычисляется значение true:
00007FF7B45110C0 test edx,edx // always 1
00007FF7B45110C2 je someTest 63h (07FF7B45110D3h)
... addresses in between omitted ...
00007FF7B45110D3 test r8d,r8d // always 0
00007FF7B45110D6 je someTest 77h (07FF7B45110E7h)
... addresses in between omitted ...
00007FF7B45110E7 test edx,edx // always 1
00007FF7B45110E9 je someTest 8Ah (07FF7B45110FAh)
... addresses in between omitted ...
00007FF7B45110FA test r8d,r8d // always 0
00007FF7B45110FD je someTest 9Eh (07FF7B451110Eh)
... addresses in between omitted ...
00007FF7B451110E test edx,edx // always 1
00007FF7B4511110 je someTest 0B1h (07FF7B4511121h)