NASM Forum > Using NASM

LEA or MOV

<< < (3/3)

fredericopissarra:

--- Quote from: munair on November 17, 2021, 09:11:34 AM ---I think it is not a good idea for the simple reason that the stack pointer may change during the execution of a function. Suppose the code within a function calls another function and (local) variables are pushed on the stack to pass them as parameters. How do you access local variables after the stack pointer has changed?
--- End quote ---

That's why compilers like C keeps track of (E|R)SP... Supose you have a funcion f() calling a function g(), each one with one argument, using i386 C callng convention:

--- Code: ---int g(int x) { return x + x; }
int f(int x) { return g(x)+1; }
--- End code ---
The generated code is something like this (without using EBP):

--- Code: ---g:
  mov eax,[esp+4]
  add  eax,eax
  ret

f:
  mov eax,[esp+4]
  push eax
  call g
  add esp,4  ; stack cleanup. keeps in a known position.
  inc eax
  ret
--- End code ---

Using the struct approach you'll always sure where arguments and local vars are for a function and don't need to remember the offsets. All you have to do is to pop the pushed argumentos from the stack after the function returns (or before...).

In some compilers (PASCAL, for instance) the responsability for stack cleanup is on the called function ("ret" accepts an argument for that)... The same come, in pascal:

--- Code: ---g:
  mov eax,[esp+4]
  add  eax,eax
  ret 4

f:
  mov eax,[esp+4]
  push eax
  call g
  inc eax
  ret 4
--- End code ---

The point is: Why use prologue/epilogue nowadays? In the old pre-386 processors if you wanted to access data on stack you had two options only: Using POP and using EBP as base pointer in an effective address. It was not possible to use registers other than BP ou BX as base pointer, and other registers than SI or DI as index (and there were no 'scale'), so using BP was mandatory. After 386 this is not the case anymore.

munair:
What is the difference between the struct approach and the stack frame approach other than keeping R/EBP free? Maybe I misunderstand. I have seen examples of GCC (32bits) output producing stack frames, so it still seems a legitimate technique.

Currently in the SharpBASIC compiler the stack cleanup is done after the function call, as it was also messed up with the push instructions before the call. Seems more logical to me, but it's a matter of opinion. Here is a simple, stupid example I used to test function calls in the expression parser:


--- Code: ---' SharpBASIC function
' -------------------
incl "lib/sys.sbi";

decl func five(n: int8): int8;

dim sum: int8;

main do
  sum = five(5 * 5) + five(5 + 5);
  print sum;
end;

func five(n: int8): int8
do
  five = n;
end;

--- End code ---

Generated asm code (without any code optimizations):


--- Code: ---
SECTION .text

global  _start
global  _end

_start:
movsx   eax, byte [_C3]
push    eax
movsx   eax, byte [_C3]
pop     edx
imul    edx
push    eax

call    _I26

add     esp, 4
push    eax
movsx   eax, byte [_C3]
push    eax
movsx   eax, byte [_C3]
pop     edx
add     eax, edx
push    eax

call    _I26

add     esp, 4
pop     edx
add     eax, edx
; save sum
mov     [_I27], al
; load sum
movsx   eax, byte [_I27]
; print int
mov     ebx, _sb_buf12
call    _sb_intstr
call    _sb_print
call    _sb_printlf

_end:
mov     ebx, 0
mov     eax, 1
int     80h

_I26:
push    ebp
mov     ebp, esp
sub     esp, 4
; init func five
mov     byte [ebp - 4], 0
; load n
movsx   eax, byte [ebp + 8]
; save func result five
mov     [ebp - 4], al
._L0:
; load func result five
movsx   eax, byte [ebp - 4]
mov     esp, ebp
pop     ebp
ret

extern  _sb_intstr
extern  _sb_print
extern  _sb_printlf
extern  _sb_buf12

SECTION .rodata

_C3                 db 5

SECTION .bss

; define sum
_I27                resb 1

--- End code ---

When you say Pascal compiler, which compiler do you mean? There are several of them, both commercial and free. Same goes for C compilers.

fredericopissarra:

--- Quote from: munair on November 17, 2021, 11:42:14 AM ---What is the difference between the struct approach and the stack frame approach other than keeping R/EBP free? Maybe I misunderstand. I have seen examples of GCC (32bits) output producing stack frames, so it still seems a legitimate technique.
--- End quote ---

Smaller and fastest code. Try to use -fomit-frame-pointer and -O2 options on GCC...


--- Quote ---When you say Pascal compiler, which compiler do you mean? There are several of them, both commercial and free. Same goes for C compilers.

--- End quote ---
Turbo Pascal, Free Pascal and Delphi... And the old "pascal" calling convention used by C compilers back in the 90's, 2000's: Turbo C, Borland C++, MSC6, ...

munair:
When I get to optimization options for the compiler, fomitting the frame pointer will probably be one of them. Thanks again for the suggestion.

Navigation

[0] Message Index

[*] Previous page

Go to full version