Форум: "Потрепаться";
Текущий архив: 2003.08.14;
Скачать: [xml.tar.bz2];
Внизdcc32, ObjectPascal (6) - языково-компиляторные концепции... Найти похожие ветки
← →
AlexRush (2003-07-28 16:44) [0]Идея данной ветки родилась в http://delphimaster.net/view/14-1057483936/ (посты от AlexRush)
Итак, прочитав ветку Юрия Зотова , я занялся небольшим исследованием работы компилятора, в частности его механизмов оптимизаци. Собствено оптимизация меня приятно удивила.
Например:
Код на паскале:
program Project1;
function ForLoopTestProc(param:CARDINAL):CARDINAL;export;
var i:integer;
begin
result:=136;
for i:=0 to 10
do begin
result:=result+param-i*(i div 2);
end;
result:=result-3;
end;
EXPORTS ForLoopTestProc;
begin
ForLoopTestProc(123);
end.
Сгенерированный БЕЗ оптимизации код:
При использовании конвенции вызовов REGISTER единственый параметр (целое, беззнаковое, 32bit)param пердается в EAX.
00401E18 public ForLoopTestProc
00401E18 ForLoopTestProc proc near ; CODE XREF: CODE:00401EB9p
00401E18
00401E18 var_C = dword ptr -0Ch ; >> PASCAL: var i:integer;
00401E18 var_8 = dword ptr -8 ; >> PASCAL: result
00401E18 var_4 = dword ptr -4 ; >> PASCAL: param
00401E18
00401E18 push ebp ;>> Стандартный пролог
00401E19 mov ebp, esp ;>> -//-
00401E1B add esp, 0FFFFFFF4h ;>> {Сам непонял, нахрена это !? кто объяснит ?}
00401E1E mov [ebp+var_4], eax ;>> { загоняет парам из EAX в "переменную" в стеке }
00401E21 mov [ebp+var_8], 88h ;>> PASCAL: result:=136;
00401E28 xor eax, eax ;>> { зануляет EAX }
00401E2A mov [ebp+var_C], eax ;>> PASCAL: {в for"е } I:=0
00401E2D
00401E2D loc_401E2D: ; CODE XREF: ForLoopTestProc+35j
00401E2D mov eax, [ebp+var_8] ;>> {загоняет result в EAX}
00401E30 add eax, [ebp+var_4] ;>> PASCAL: result+param
00401E33 mov edx, [ebp+var_C] ;>> { I в edx }
00401E36 sar edx, 1 ;>> PASCAL: (i div 2)
00401E38 jns short loc_401E3D
00401E3A adc edx, 0 ;>> {т.к. результат - БЕЗзнаковое}
00401E3D
00401E3D loc_401E3D: ; CODE XREF: ForLoopTestProc+20j
00401E3D imul edx, [ebp+var_C] ;>> PASCAL: i*(i div 2)
00401E41 sub eax, edx ;>> result + ( param-i*(i div 2) )
00401E43 mov [ebp+var_8], eax ;>> result := ( result+param-i*(i div 2) )
00401E46 inc [ebp+var_C] ;>> { инкримент I }
00401E49 cmp [ebp+var_C], 0Bh ;>> PASCAL: {в for"е} to 10
00401E4D jnz short loc_401E2D ;>> PASCAL: do
00401E4F sub [ebp+var_8], 3 ;>> PASCAL: result:=result-3;
00401E53 mov eax, [ebp+var_8] ;>> { возвращаемое значение - в EAX }
00401E56 mov esp, ebp ; стандартный эпилог
00401E58 pop ebp ; -//-
00401E59 retn ( даже без оптимизации)Идея данной ветки родилась в http://delphimaster.net/view/14-1057483936/ (посты от AlexRush)
Итак, прочитав ветку Юрия Зотова , я занялся небольшим исследованием работы компилятора, в частности его механизмов оптимизаци. Собствено оптимизация меня приятно удивила.
Например:
Код на паскале:
program Project1;
function ForLoopTestProc(param:CARDINAL):CARDINAL;export;
var i:integer;
begin
result:=136;
for i:=0 to 10
do begin
result:=result+param-i*(i div 2);
end;
result:=result-3;
end;
EXPORTS ForLoopTestProc;
begin
ForLoopTestProc(123);
end.
Сгенерированный БЕЗ оптимизации код:
При использовании конвенции вызовов REGISTER единственый параметр (целое, беззнаковое, 32bit)param пердается в EAX.
00401E18 public ForLoopTestProc
00401E18 ForLoopTestProc proc near ; CODE XREF: CODE:00401EB9p
00401E18
00401E18 var_C = dword ptr -0Ch ; >> PASCAL: var i:integer;
00401E18 var_8 = dword ptr -8 ; >> PASCAL: result
00401E18 var_4 = dword ptr -4 ; >> PASCAL: param
00401E18
00401E18 push ebp ;>> Стандартный пролог
00401E19 mov ebp, esp ;>> -//-
00401E1B add esp, 0FFFFFFF4h ;>> {Сам непонял, нахрена это !? кто объяснит ?}
00401E1E mov [ebp+var_4], eax ;>> { загоняет парам из EAX в "переменную" в стеке }
00401E21 mov [ebp+var_8], 88h ;>> PASCAL: result:=136;
00401E28 xor eax, eax ;>> { зануляет EAX }
00401E2A mov [ebp+var_C], eax ;>> PASCAL: {в for"е } I:=0
00401E2D
00401E2D loc_401E2D: ; CODE XREF: ForLoopTestProc+35j
00401E2D mov eax, [ebp+var_8] ;>> {загоняет result в EAX}
00401E30 add eax, [ebp+var_4] ;>> PASCAL: result+param
00401E33 mov edx, [ebp+var_C] ;>> { I в edx }
00401E36 sar edx, 1 ;>> PASCAL: (i div 2)
00401E38 jns short loc_401E3D
00401E3A adc edx, 0 ;>> {т.к. результат - БЕЗзнаковое}
00401E3D
00401E3D loc_401E3D: ; CODE XREF: ForLoopTestProc+20j
00401E3D imul edx, [ebp+var_C] ;>> PASCAL: i*(i div 2)
00401E41 sub eax, edx ;>> result + ( param-i*(i div 2) )
00401E43 mov [ebp+var_8], eax ;>> result := ( result+param-i*(i div 2) )
00401E46 inc [ebp+var_C] ;>> { инкримент I }
00401E49 cmp [ebp+var_C], 0Bh ;>> PASCAL: {в for"е} to 10
00401E4D jnz short loc_401E2D ;>> PASCAL: do
00401E4F sub [ebp+var_8], 3 ;>> PASCAL: result:=result-3;
00401E53 mov eax, [ebp+var_8] ;>> { возвращаемое значение - в EAX }
00401E56 mov esp, ebp ; стандартный эпилог
00401E58 pop ebp ; -//-
00401E59 retn ; >> PASCAL: end;
00401E59 ForLoopTestProc endp
Приятно удивило то, что вместо DIV для деления на 2 компилятор использует SAR (даже без оптимизации).
...............
← →
AlexRush (2003-07-28 16:45) [1]Сгенерированный С оптимизацией код:
00401E18 public ForLoopTestProc
00401E18 ForLoopTestProc proc near ; CODE XREF: CODE:00401E9Dp
00401E18 push ebx : >> {сохраняет ebx}
00401E19 mov ecx, 88h ; >> PASCAL: result:=136;
00401E1E xor edx, edx ; >> PASCAL: {в for"е -} I:=0
00401E20
00401E20 loc_401E20: ; CODE XREF: ForLoopTestProc+1Dj
00401E20 lea ecx, [eax+ecx] ; >> PASCAL: result:=result+param {param передается в EAX}
00401E23 mov ebx, edx
00401E25 sar ebx, 1 ; >> PASCAL: (i div 2)
00401E27 jns short loc_401E2C
00401E29 adc ebx, 0 ; >> {т.к. результат - БЕЗзнаковое}
00401E2C
00401E2C loc_401E2C: ; CODE XREF: ForLoopTestProc+Fj
; >> PASCAL: result:=result+param-i*(i div 2);
00401E2C imul ebx, edx ; >> i * (i div 2)
; >> // ECX {result} | EBX
00401E2F sub ecx, ebx ; >> ( result:=result+param ) - ( i * (i div 2) )
00401E31 inc edx ; >> {инкримент в for"е}
00401E32 cmp edx, 0Bh ; >> PASCAL: {for i:=0 } to 10
00401E35 jnz short loc_401E20 ; >> PASCAL: {for i:=0 } to 10
00401E37 sub ecx, 3 ; >> PASCAL: result:=result-3;
00401E3A mov eax, ecx ; >> результат возвращается в EAX
00401E3C pop ebx ; >> {восстанавливает EBX}
00401E3D retn ; >> PASCAL: end;
00401E3D ForLoopTestProc endp
Теперь рассмотрим подробнее сгенерированый код.
Регистр EBX компилятор использует в своих целях и посему сохраняет его в начале ф-и.
Для хранения result в даном случае исп. ECX, для переменной I - EDX.
Основные моменты оптимизации:
Все "переменные" хранятся в регистрах, а не в памяти.
Таким образом не используются лишние "гоняния" из памяти в регистры и обратно.
00401E20 lea ( Load Effective Address)Сгенерированный С оптимизацией код:
00401E18 public ForLoopTestProc
00401E18 ForLoopTestProc proc near ; CODE XREF: CODE:00401E9Dp
00401E18 push ebx : >> {сохраняет ebx}
00401E19 mov ecx, 88h ; >> PASCAL: result:=136;
00401E1E xor edx, edx ; >> PASCAL: {в for"е -} I:=0
00401E20
00401E20 loc_401E20: ; CODE XREF: ForLoopTestProc+1Dj
00401E20 lea ecx, [eax+ecx] ; >> PASCAL: result:=result+param {param передается в EAX}
00401E23 mov ebx, edx
00401E25 sar ebx, 1 ; >> PASCAL: (i div 2)
00401E27 jns short loc_401E2C
00401E29 adc ebx, 0 ; >> {т.к. результат - БЕЗзнаковое}
00401E2C
00401E2C loc_401E2C: ; CODE XREF: ForLoopTestProc+Fj
; >> PASCAL: result:=result+param-i*(i div 2);
00401E2C imul ebx, edx ; >> i * (i div 2)
; >> // ECX {result} | EBX
00401E2F sub ecx, ebx ; >> ( result:=result+param ) - ( i * (i div 2) )
00401E31 inc edx ; >> {инкримент в for"е}
00401E32 cmp edx, 0Bh ; >> PASCAL: {for i:=0 } to 10
00401E35 jnz short loc_401E20 ; >> PASCAL: {for i:=0 } to 10
00401E37 sub ecx, 3 ; >> PASCAL: result:=result-3;
00401E3A mov eax, ecx ; >> результат возвращается в EAX
00401E3C pop ebx ; >> {восстанавливает EBX}
00401E3D retn ; >> PASCAL: end;
00401E3D ForLoopTestProc endp
Теперь рассмотрим подробнее сгенерированый код.
Регистр EBX компилятор использует в своих целях и посему сохраняет его в начале ф-и.
Для хранения result в даном случае исп. ECX, для переменной I - EDX.
Основные моменты оптимизации:
Все "переменные" хранятся в регистрах, а не в памяти.
Таким образом не используются лишние "гоняния" из памяти в регистры и обратно.
00401E20 lea ecx, [eax+ecx] ; >> PASCAL: result:=result+param {param передается в EAX}
Команда LEA (Load Effective Address) изначально предназначена, как видно из названия, для загрузки эффективного адреса.
Любой программист, пишущий на асме, знает, что ее удобно использовать для трехоперандного сложения.
(что и делает компилятор, за что честь ему и хвала :)
И в целом аглоритм вышел компактнее и красивее. На мой взгляд даже читабельнее.
........to be continued... {курить пора}
← →
AlexRush (2003-07-28 16:58) [2]пример реализации CASE ... OF см. http://delphimaster.net/view/14-1057483936/
Там же (next post) и мой вопрос.
Лично я не вижу причин (уж технических, так точно), по которым нельзя добавить возможность использования переменых в CASE.
Кто что может сказать по этому поводу ?
P.S. А так же по другим вопросам subj....
← →
AlexRush (2003-07-28 18:14) [3]Как видно из предыдущих примеров, под переменную счетчика I использовался регистр EDX. Кроме того, param, согласно REGISTER передается в EAX.
Изменим код ForLoopTestProc, чтобы EAX и EDX были задействованы в теле цикла:
program Project1;
function ForLoopTestProc(param:CARDINAL):CARDINAL;export;
var i:integer;
begin
result:=136;
for i:=0 to 10
do asm
mov edx,i
add eax,3
add result,edx
end;
result:=result-3;
end;
EXPORTS ForLoopTestProc;
begin
ForLoopTestProc(123);
end.
Код, сгенерированый с оптимизацией:
00401E18 public ForLoopTestProc
00401E18 ForLoopTestProc proc near ; CODE XREF: CODE:00401EA5p
00401E18
00401E18 var_8 = dword ptr -8 ;>> result
00401E18 var_4 = dword ptr -4 ;>> PASCAL: var i:integr;
00401E18
00401E18 push ebp ;>> Пролог
00401E19 mov ebp, esp ;>> -//-
00401E1B add esp, 0FFFFFFF8h ;>> А, блин, понял :) просто я привык здесь SUB использовать, и провтыкал :)
00401E1E mov [ebp+var_8], 88h ;>> PASCAL: result:=136;
00401E25 xor eax, eax ;>> { обнуление EAX }
00401E27 mov [ebp+var_4], eax ;>> PASCAL: {в for"е} I:=0
00401E2A
00401E2A loc_401E2A: ; CODE XREF: ForLoopTestProc+22j
00401E2A mov edx, [ebp+var_4] ;>> PASCAL: mov edx,i
00401E2D add eax, 3 ;>> PASCAL: add eax,3
00401E30 add [ebp+var_8], edx ;>> PASCAL: add result,eax
00401E33 inc [ebp+var_4] ;>> { инкримент I }
00401E36 cmp [ebp+var_4], 0Bh ;>> PASCAL: {в for"е} to 10
00401E3A jnz short loc_401E2A ;>> PASCAL: do
00401E3C sub [ebp+var_8], 3 ;>> PASCAL: result:=result-3
00401E40 mov eax, [ebp+var_8] ;>> { возвращаемое значение в EAX }
;>> А ВОТ ЭТОГО Я В ПРИНЦЫПЕ НЕ ПОНЯЛ !!!! ???
00401E43 pop ecx
00401E44 pop ecx
00401E45 pop ebp
00401E46 retn
00401E46 ForLoopTestProc endp
Так как EAX и EDX заняты, компилятор использует стек для хранения I и result. Как можно себе представить,
inc [ebp+var_4] ;>> { инкримент I }
cmp [ebp+var_4], 0Bh ;>> PASCAL: {в for"е} to 10
работает медленнее, чем
inc edx ; >> {инкримент в for"е}
сmp edx, 0Bh ; >> PASCAL: {for i:=0 } to 10
(
для INC:
Operands Bytes Clocks
r8 2 1 UV
r16 1 1 UV
r32 1 1 UV
mem 2+d(0,2) 3 UV
для CMP:
Operands Bytes Clocks
reg, reg 2 1 UV
mem, reg 2+d(0,2) 2 UV
reg, mem 2+d(0,2) 2 UV
reg, imm 2+i(1,2) 1 UV
mem, imm 2+d(0,2)+i(1,2) 2 UV*
acc, imm ( 1,2)Как видно из предыдущих примеров, под переменную счетчика I использовался регистр EDX. Кроме того, param, согласно REGISTER передается в EAX.
Изменим код ForLoopTestProc, чтобы EAX и EDX были задействованы в теле цикла:
program Project1;
function ForLoopTestProc(param:CARDINAL):CARDINAL;export;
var i:integer;
begin
result:=136;
for i:=0 to 10
do asm
mov edx,i
add eax,3
add result,edx
end;
result:=result-3;
end;
EXPORTS ForLoopTestProc;
begin
ForLoopTestProc(123);
end.
Код, сгенерированый с оптимизацией:
00401E18 public ForLoopTestProc
00401E18 ForLoopTestProc proc near ; CODE XREF: CODE:00401EA5p
00401E18
00401E18 var_8 = dword ptr -8 ;>> result
00401E18 var_4 = dword ptr -4 ;>> PASCAL: var i:integr;
00401E18
00401E18 push ebp ;>> Пролог
00401E19 mov ebp, esp ;>> -//-
00401E1B add esp, 0FFFFFFF8h ;>> А, блин, понял :) просто я привык здесь SUB использовать, и провтыкал :)
00401E1E mov [ebp+var_8], 88h ;>> PASCAL: result:=136;
00401E25 xor eax, eax ;>> { обнуление EAX }
00401E27 mov [ebp+var_4], eax ;>> PASCAL: {в for"е} I:=0
00401E2A
00401E2A loc_401E2A: ; CODE XREF: ForLoopTestProc+22j
00401E2A mov edx, [ebp+var_4] ;>> PASCAL: mov edx,i
00401E2D add eax, 3 ;>> PASCAL: add eax,3
00401E30 add [ebp+var_8], edx ;>> PASCAL: add result,eax
00401E33 inc [ebp+var_4] ;>> { инкримент I }
00401E36 cmp [ebp+var_4], 0Bh ;>> PASCAL: {в for"е} to 10
00401E3A jnz short loc_401E2A ;>> PASCAL: do
00401E3C sub [ebp+var_8], 3 ;>> PASCAL: result:=result-3
00401E40 mov eax, [ebp+var_8] ;>> { возвращаемое значение в EAX }
;>> А ВОТ ЭТОГО Я В ПРИНЦЫПЕ НЕ ПОНЯЛ !!!! ???
00401E43 pop ecx
00401E44 pop ecx
00401E45 pop ebp
00401E46 retn
00401E46 ForLoopTestProc endp
Так как EAX и EDX заняты, компилятор использует стек для хранения I и result. Как можно себе представить,
inc [ebp+var_4] ;>> { инкримент I }
cmp [ebp+var_4], 0Bh ;>> PASCAL: {в for"е} to 10
работает медленнее, чем
inc edx ; >> {инкримент в for"е}
сmp edx, 0Bh ; >> PASCAL: {for i:=0 } to 10
(
для INC:
Operands Bytes Clocks
r8 2 1 UV
r16 1 1 UV
r32 1 1 UV
mem 2+d(0,2) 3 UV
для CMP:
Operands Bytes Clocks
reg, reg 2 1 UV
mem, reg 2+d(0,2) 2 UV
reg, mem 2+d(0,2) 2 UV
reg, imm 2+i(1,2) 1 UV
mem, imm 2+d(0,2)+i(1,2) 2 UV*
acc, imm 1+i(1,2) 1 UV
)
...Ввиду чего становится понятным предостережение от необоснованого использования ассемблера в Паскале.
← →
AlexRush (2003-07-28 19:48) [4]Что-то меня понесло под вечер... :)
Еще возникла идея протестировать отношение компилятора к разным конвенциям вызовов.
Пусть имеем функцию:
function VarParamAndCallingConventionTestProc(param:CARDINAL):CARDINAL;export;
begin
result:=param+136;
end;
В данном случае в EAX передается само значение param:
00401E18 public VarParamAndCallingConventionTestProc
00401E18 VarParamAndCallingConventionTestProc proc near ; CODE XREF: CODE:00401E7Dp
00401E18 add eax, 88h ;>> PASCAL: result:=param+136;
00401E1D retn
00401E1D VarParamAndCallingConventionTestProc endp
Лучшей реализации ожидать было, пожалуй, невозможно.
Объявим теперь параметр param как VAR:
program Project1;
function VarParamAndCallingConventionTestProc(var param:CARDINAL):CARDINAL;export;
begin
result:=param+136;
end;
EXPORTS VarParamAndCallingConventionTestProc;
var dw:CARDINAL;
begin
dw:=123;
VarParamAndCallingConventionTestProc(dw);
end.
А здесь в EAX передается теперь уже (оно и понятно :) адрес dw:
00401E18 public VarParamAndCallingConventionTestProc
00401E18 VarParamAndCallingConventionTestProc proc near ; CODE XREF: CODE:00401E87p
00401E18 mov eax, [eax] ;загружает в EAX операнд, находящийся по адресу [EAX]
00401E1A add eax, 88h ;>> PASCAL: result:=param+136;
00401E1F retn
00401E1F VarParamAndCallingConventionTestProc endp
А теперь самое интересное. Изменим конвенцию вызовов на STDCALL:
function VarParamAndCallingConventionTestProc(var param:CARDINAL):CARDINAL;stdcall;export;
begin
result:=param+136;
end;
В результате получим:
public VarParamAndCallingConventionTestProc
00401E18 VarParamAndCallingConventionTestProc proc near ; CODE XREF: CODE:00401E93p
00401E18
00401E18 arg_0 = dword ptr 8 ;>> { параметр param в стеке [ESP+8]}
00401E18
00401E18 push ebp ;>> { стандартный пролог для stdcall !!! }
00401E19 mov ebp, esp ;>> -//-
00401E1B mov eax, [ebp+arg_0] ;>> загружает в EAX param
00401E1E mov eax, [eax] ;>> загружает в EAX операнд, находящийся по адресу [EAX]
00401E20 add eax, 88h ;>> PASCAL: result:=param+136;
00401E25 pop ebp ;>> { стандартный эпилог }
00401E26 retn 4 ( 4 байта)Что-то меня понесло под вечер... :)
Еще возникла идея протестировать отношение компилятора к разным конвенциям вызовов.
Пусть имеем функцию:
function VarParamAndCallingConventionTestProc(param:CARDINAL):CARDINAL;export;
begin
result:=param+136;
end;
В данном случае в EAX передается само значение param:
00401E18 public VarParamAndCallingConventionTestProc
00401E18 VarParamAndCallingConventionTestProc proc near ; CODE XREF: CODE:00401E7Dp
00401E18 add eax, 88h ;>> PASCAL: result:=param+136;
00401E1D retn
00401E1D VarParamAndCallingConventionTestProc endp
Лучшей реализации ожидать было, пожалуй, невозможно.
Объявим теперь параметр param как VAR:
program Project1;
function VarParamAndCallingConventionTestProc(var param:CARDINAL):CARDINAL;export;
begin
result:=param+136;
end;
EXPORTS VarParamAndCallingConventionTestProc;
var dw:CARDINAL;
begin
dw:=123;
VarParamAndCallingConventionTestProc(dw);
end.
А здесь в EAX передается теперь уже (оно и понятно :) адрес dw:
00401E18 public VarParamAndCallingConventionTestProc
00401E18 VarParamAndCallingConventionTestProc proc near ; CODE XREF: CODE:00401E87p
00401E18 mov eax, [eax] ;загружает в EAX операнд, находящийся по адресу [EAX]
00401E1A add eax, 88h ;>> PASCAL: result:=param+136;
00401E1F retn
00401E1F VarParamAndCallingConventionTestProc endp
А теперь самое интересное. Изменим конвенцию вызовов на STDCALL:
function VarParamAndCallingConventionTestProc(var param:CARDINAL):CARDINAL;stdcall;export;
begin
result:=param+136;
end;
В результате получим:
public VarParamAndCallingConventionTestProc
00401E18 VarParamAndCallingConventionTestProc proc near ; CODE XREF: CODE:00401E93p
00401E18
00401E18 arg_0 = dword ptr 8 ;>> { параметр param в стеке [ESP+8]}
00401E18
00401E18 push ebp ;>> { стандартный пролог для stdcall !!! }
00401E19 mov ebp, esp ;>> -//-
00401E1B mov eax, [ebp+arg_0] ;>> загружает в EAX param
00401E1E mov eax, [eax] ;>> загружает в EAX операнд, находящийся по адресу [EAX]
00401E20 add eax, 88h ;>> PASCAL: result:=param+136;
00401E25 pop ebp ;>> { стандартный эпилог }
00401E26 retn 4 ;>> { возврат с удалением параметра из стека (4 байта)}
00401E26 VarParamAndCallingConventionTestProc endp
Итого: Стек (push"ы и pop"ы) внутри тела ф-и не используется, тем не менее компилятор вставляет
стандартные при использовании stdcall пролог и эпилог, что обычно делается для организации
доступа к параметрам (и в общем случае к локальным переменным) и одновременного использования push/pop операций.
Вот этот момент уже не совсем радует. Интересно, почему разработчики компилятора не
предусмотрели этот вариант ? Хотя,возможно, реальных функций, в которых не используются PUSH/POP бывает крайне мало.
Может просто принебрегли...
← →
uw (2003-07-28 21:04) [5]>{Сам непонял, нахрена это !? кто объяснит ?}
Резервирует 12 ячеек в стеке для локальных переменных.
0FFFFFFF4h есть -12.
← →
VD602 (2003-07-28 22:32) [6]Для поддержки темы еще один вопрос:
Что помещается в стек при вызове процедуры кроме аргументов? При просмотре в CPU-window там довольно много непонятных (только для меня) значений.
И зачем компилятор генерирует там же (при вызове процедуры) строки
push $00
причем бывает по четыре(!) за раз.
Вот этого точно понять не могу.
← →
AlexRush (2003-07-29 13:05) [7]>uw © (28.07.03 21:04) - Спасибо, я уже догадался (см. AlexRush © (28.07.03 18:14) просто я привык видеть здесь SUB а не ADD.
>VD602 (28.07.03 22:32)
при вызове подпрограммы (процедуры) командой CALL помешается адрес возврата (следующей за CALL команды). Он лежит на вершине стека (над параметрами)Его использует команда RET (RETN/RETF) для возврата в вызывающую процедуру. Так же стек используется для хранения локальных переменных.
Пример:
[00] ; пролог
[01] push ebp ; Сохранение EBP
[02] mov ebp, esp ; Сохранение значения ESP в EBP
[03] sub esp, 0Ch ; Увеличиваем кадр стека на 12 байт (например для 3х integer"ов)
[04] .....
[05] здесь полезная работа, в том числе можно использовать [0] PUSH/POP.
[06] К параметрам и переменным в стеке обращаемся через [0] [EBP±offset]
[07] .....
[08] ; эпилог
[09] mov esp,ebp ; разрушаем созданый стековый кадр
[10] pop ebp ( пролог и эпилог процедуры)>uw © (28.07.03 21:04) - Спасибо, я уже догадался (см. AlexRush © (28.07.03 18:14) просто я привык видеть здесь SUB а не ADD.
>VD602 (28.07.03 22:32)
при вызове подпрограммы (процедуры) командой CALL помешается адрес возврата (следующей за CALL команды). Он лежит на вершине стека (над параметрами)Его использует команда RET (RETN/RETF) для возврата в вызывающую процедуру. Так же стек используется для хранения локальных переменных.
Пример:
[00] ; пролог
[01] push ebp ; Сохранение EBP
[02] mov ebp, esp ; Сохранение значения ESP в EBP
[03] sub esp, 0Ch ; Увеличиваем кадр стека на 12 байт (например для 3х integer"ов)
[04] .....
[05] здесь полезная работа, в том числе можно использовать [0] PUSH/POP.
[06] К параметрам и переменным в стеке обращаемся через [0] [EBP±offset]
[07] .....
[08] ; эпилог
[09] mov esp,ebp ; разрушаем созданый стековый кадр
[10] pop ebp ; восстанавливаем EBP
[11]
[12] retn
Наборы команд [01-03] и [09-10] (пролог и эпилог процедуры) настолько часто применялись, что в 80186 были введены специальные команды ENTER и LEAVE соответственно.
Обо всем этом хорошо написано у Зубкова С.В. "Assembler для DOS, Windows, UNIX" и неплохо у Мета Питрека "Секреты системного прогаммирования для W95"
Страницы: 1 вся ветка
Форум: "Потрепаться";
Текущий архив: 2003.08.14;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.006 c