When you use "proc" to define a procedure by default masm replaces "ret" with leave and ret <number> (if stdcall). I've read in Intel's Opt Guide that it's better not to use leave. How I suppose to make masm not to replace "ret"?? Thanks.
P.S.: I know that you can do by leaving the "proc" and using label, but in that way the code is really messy and if you have to rewrite something you are dead.
This code times procedures with and without leave (and the corresponding push ebp / mov ebp, esp).
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
.586
include timers.asm
wleave PROTO :DWORD,:DWORD
woleave PROTO :DWORD,:DWORD
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
LOOP_COUNT equ 10000000
counter_begin LOOP_COUNT, HIGH_PRIORITY_CLASS
invoke wleave, 1, 2
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
counter_begin LOOP_COUNT, HIGH_PRIORITY_CLASS
invoke woleave, 1, 2
counter_end
print ustr$(eax)
print chr$(" cycles",13,10)
inkey "Press any key to exit..."
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
nops 4
align 4
wleave proc arg1:DWORD, arg2:DWORD
mov eax, arg1
mov edx, arg2
ret
wleave endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
align 4
woleave proc arg1:DWORD, arg2:DWORD
mov eax, [esp+4]
mov edx, [esp+8]
ret 8
woleave endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
(The four nops make the procedures easier to find in the disassembly)
004011FD 90 nop
004011FE 90 nop
004011FF 90 nop
00401200 90 nop
00401201 8D4900 lea ecx,[ecx]
00401204 fn_00401204:
00401204 55 push ebp
00401205 8BEC mov ebp,esp
00401207 8B4508 mov eax,[ebp+8]
0040120A 8B550C mov edx,[ebp+0Ch]
0040120D C9 leave
0040120E C20800 ret 8
00401211 8D4900 lea ecx,[ecx]
00401214 fn_00401214:
00401214 8B442404 mov eax,[esp+4]
00401218 8B542408 mov edx,[esp+8]
0040121C C20800 ret 8
On my P3 the advantage is 2 clock cycles, IMO meaningful only for very fast procedures.
5 cycles
3 cycles
[attachment deleted by admin]
Quote from: MichaelW on January 08, 2006, 12:11:34 AM
wleave proc arg1:DWORD, arg2:DWORD
mov eax, arg1
mov edx, arg2
ret
wleave endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
align 4
woleave proc arg1:DWORD, arg2:DWORD
mov eax, [esp+4]
mov edx, [esp+8]
ret 8
woleave endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
If you don't have any local variables declared in your procedure then this works fine. Otherwise, if you don't use the default epilogue or the LEAVE opcode, you need to add this to the end of the proc:
mov esp, ebp
pop ebx
Which is exactly what LEAVE does, except porting to 64-bit code won't require changing the LEAVE opcode, whereas the code above will need RSP and RBP. I also think LEAVE will throw extra exceptions if there is invalid data anywhere, but I haven't checked nor confirmed this.
Cheers,
Zooba :U
Here is a test piece I did for Pelle and on the PIV I use there is no meaningful timing difference.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
comment * -----------------------------------------------------
Build this template with
"CONSOLE ASSEMBLE AND LINK"
----------------------------------------------------- *
lcnt equ <300000000>
.code
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
call main
inkey
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
main proc
LOCAL total1 :DWORD
LOCAL total2 :DWORD
invoke SetPriorityClass,FUNC(GetCurrentProcess),REALTIME_PRIORITY_CLASS
mov total1, 0
mov total2, 0
REPEAT 8
; =============
push esi
mov esi, lcnt
invoke GetTickCount
push eax
@@:
call with
sub esi, 1
jnz @B
invoke GetTickCount
pop ecx
sub eax, ecx
add total1, eax
print str$(eax)," leave",13,10
invoke SleepEx,100,0
; =============
mov esi, lcnt
invoke GetTickCount
push eax
@@:
call without
sub esi, 1
jnz @B
invoke GetTickCount
pop ecx
sub eax, ecx
add total2, eax
print str$(eax)," mov-pop",13,10
invoke SleepEx,100,0
pop esi
; =============
ENDM
invoke SetPriorityClass,FUNC(GetCurrentProcess),NORMAL_PRIORITY_CLASS
shr total1, 3
shr total2, 3
print str$(total1)," average for LEAVE",13,10
print str$(total2)," average for MOV-POP",13,10
ret
main endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
align 16
with:
push ebp
mov ebp, esp
sub esp, 16
nop
nop
nop
nop
leave
retn
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
align 16
without:
push ebp
mov ebp, esp
sub esp, 16
nop
nop
nop
nop
mov esp, ebp
pop ebp
retn
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
Thanks for the help guys. Appreciated!! The "OPTION EPILOGUE:NONE" did the trick.