Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 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
14-44991
Vlad Oshin
2003-07-28 10:07
2003.08.14
электрик Володя. Рассказ.


14-45114
Dankin
2003-06-03 12:35
2003.08.14
Drag and drop


14-45005
PsoFT
2003-07-29 12:26
2003.08.14
Привет !


14-45100
BJValentine
2003-06-12 17:14
2003.08.14
Клики мыши


14-44984
Region
2003-07-29 23:43
2003.08.14
Флэш и Дельфи.





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский