Исходный текст транслирован NASM-мом. В дизассемблированном тексте встретил такую конструкция: ....... mov [ebp+var_4], eax mov eax, [ebp+var_4] ...... В чем смысл?
Адрес, на который часто происходит переход (например начало цикла), желательно иметь кратным 4,8,16. Вот и выравнивают компиляторы адрес, вставляя перед циклом команды, ничего не делающие, но позволяющие сдвинуть адрес цикла на ближайшее кратное align'у значение.
cresta А к примеру будет такая сутуация: mov eax,2d jmp metka1 mov eax,3d metka1: Сразу видно что выполняться, будет код после metka1, но ведь команда mov eax,3d по идее уже попала, т.к. за 1 проход: 1. выбор следующей команды 2. декодирование выбранной прошешим шагом 3. вполнение текущей Мне до сих пор не понятно как процессор умудряется не выполнить подобные mov eax,3d команды?
EvilsInterrupt 1. выбор следующей команды 2. декодирование выбранной прошешим шагом 3. вполнение текущей - этот алгоритм выполняет конвеер команд, только там шагов больше (6). "Мне до сих пор не понятно как процессор умудряется не выполнить подобные mov eax,3d команды?" Если рассматривать традиционный конвеер команд, то всё очень просто (6 шагов). А если взглянуть на нынешние процессоры с суперконвеерной системой, то там немного сложнее (более 6 шагов). А выполнить он их не сможет, так как командой перехода "jmp metka1" адрес следующей команды уже формируется, соответственно "mov eax,3d" на выполнение не попадает.
cresta "Адрес, на который часто происходит переход (например начало цикла), желательно иметь кратным 4,8,16. Вот и выравнивают компиляторы адрес, вставляя перед циклом команды, ничего не делающие, но позволяющие сдвинуть адрес цикла на ближайшее кратное align'у значение." В таких ситуациях чаще выравнивают командной nop
Какой командой выравнивать - это решает компилятор, исходя из заложенных в него шаблонов. У каждого компилятора свои шаблоны, и они зависят от количества байт, на которое надо сдвинуть адрес. А лепить кучу nop'ов - этого практически никто не делает. Вот выравнивание от ml.exe: Вот процедура Код (Text): cicle proc xor eax,eax mov ecx,10000h _m: mov eax,ecx shl eax,2 dec ecx jnz _m ret cicle endp Вот её дизассемблерный лист: Код (Text): 00401030 XOR EAX,EAX 00401032 MOV ECX,10000 [b]00401037[/b] MOV EAX,ECX ;адрес некратный 16 00401039 SHL EAX,2 0040103C DEC ECX 0040103D JNZ SHORT ShExp.00401037 0040103F RETN Вот та же процедура с выравненым циклом: Код (Text): cicle proc xor eax,eax mov ecx,10000h [b]align 16[/b] _m: mov eax,ecx shl eax,2 dec ecx jnz _m ret cicle endp И её листинг: Код (Text): 00401030 XOR EAX,EAX 00401032 MOV ECX,10000 00401037 LEA ESP,DWORD PTR SS:[ESP] 0040103E MOV EDI,EDI [b]00401040[/b] MOV EAX,ECX 00401042 SHL EAX,2 00401045 DEC ECX 00401046 JNZ SHORT ShExp.00401040 00401048 RETN Во втором коде, чтобы выравнять начало цикла на 16, компилятор вставил две инструкции LEA ESP,DWORD PTR SS:[ESP] и MOV EDI,EDI, которые не влияют на регистры и на флаги. Т.е. они будут выполняться, но фактически ничего не делают, и за счет их длины начало цикла сместилось с некратного 16 адреса 00401037 на 00401040.
Может я и не прав по NOP'ам (много nop'ов - много выборки). Но та конструкция выравнивает по 6, а не по 4, 8, 16, ... Хотя, возможно, что это ЧАСТЬ конструкции, которая выравнивает по 4, 8 или 16.
Это какая конструкция? Которую привел автор? Сказать на сколько невозможно не имея окружающего кода. Смещать может на 6 байт, а вот выравнивать следующую - это на адрес+6
Tiro Код скомпилирован без оптимизации. cresta Для выравнивания используются инструкции типа nop, mov edi,edi и т.д. "mov eax, [ebp+var_4]" после "mov [ebp+var_4], eax" будет только тормозить.
Tiro Вторая инструкция возможно является целевым адресом перехода и может выполняться не только непосредственно после первой. Также возможно код был сгенерирован из "тупых макросов".
Компилятор я указал поспешно. Возможно это C. Вся процедура: push ebp mov ebp, esp push ecx push ebx call _dsGetCurSmp@0 sub eax, ds:dword_1D5A mov [ebp+var_4], eax mov eax, [ebp+var_4] mov ebx, 3E8h imul ebx mov ebx, 2B110h idiv ebx mov [ebp+var_4], eax mov eax, [ebp+var_4] pop ebx leave retn .bss:00001D5A dword_1D5A dd ?
push ecx push ebx call _dsGetCurSmp@0 sub eax, ds:dword_1D5A mov [ebp+var_4], eax mov eax, [ebp+var_4] mov ebx, 3E8h imul ebx Налицо просто явное отсутствие оптимизации между двумя высокоуровневыми конструкциями (call и imul) - больше никакого тайного смысла здесь нет. Во втором случае - то же самое. Исходно программистом код был написан с задействованием локальной переменной, а компилятор не смог понять, что можно обойтись без нее.
Скорее всего, исходный код выглядел так: return (dsGetCurSmp() - dword_1D5A)*1000/176400; Компилятор при этом заводит временную локальную переменную (или кладет в стек временное значение). Поэтому заморочек, связанных с выравниванием, здесь нет.