News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Proc with and without a parameter

Started by gabor, October 17, 2006, 02:18:14 PM

Previous topic - Next topic

gabor

Hello everyone!

Since a while I've been thinking about the use and possibility of creating procedures that can be called either with parameters or with args passed in registers.
Example:

A smart and usefull little object:

Object          STRUCT
                important       dd ?
                stuff           db 10 dup (?)
                toBe            dd ?
                stored          dd ?
Object          ENDS


and a method:


;DoStuff         PROTO lpObject:DWORD,lpStuff:DWORD



DoStuff:
; generall part
                push    ebp
                mov     eax,esp
                mov     ebp,eax

; read arguments
                mov     esi,[ebp+08h]           ; lpObject
                mov     edx,[ebx+0Ch]           ; lpStuff

@DoStuff:

; put stuff from lpStuff into Object.stuff
                xor     ecx,ecx
@@:
                mov     al,[edx+ecx]
                or      al,al
                jz @F
                mov     (Object PTR [esi+ecx]).stuff,al
                add     ecx,1
                cmp     ecx,sizeof Object.stuff
                jc @B
@@:
                pop ebp
                ret 8


The point would be that the procedure can be called
- by setting esi and edx and then calling the @DoStuffM: part
or
- normally, by passing both arguments on the stack


For the first usage a macro could be applied:

DoStuffM        MACRO

                add     esp,8                   ; simulate passing over on the stack
                call @DoStuff                   ; this is a label inside the proc
ENDM


I am quite uncertain about this. It happens several times that I would need a general way and also an "internal" way of calling a procedure. The goal is to improve performance by minimizing procedure calls and stack accesses. (If a pointer is already in esi, why pass it on the stack just to be set into esi again...)

The methods of an object can call each other. Since inside these methods the pointer of the object is generally in the same register (usually esi) it is a waste to pass esi over and over again. But on the other hand it is necessary to be able to call the methods without the object's pointer set in esi.


                dumyObject      Object <>
                stuff           db 1,2,3,4,5,6,7,8,9,0

...

                invoke DoStuff offset dumyObject,offset stuff
; or
; esi has already been set to point dumyObject
                mov             edx,offset stuff
                call @DoStuff




ps: If it hasn't got clear this object and its method are examples only. I could think of more complex cases...

Greets, Gábor

Biterider

Hi Gábor
You will not find an absolute answer to your dilemma, it is more a matter of the architecture you choose. C++ and ATC passes the instance pointer in ecx always, COM and ObjAsm32 uses the stack. You have to think that if you write reusable code it is better to make no assumptions about the registers to achieve the best flexibility for further extensions.

Regards,

Biterider

Tedd

It is an annoyance sometimes that you can see/imagine these optimisations but can't do anything about it without losing generality (if you make it too specific then it can't be used as a 'normal' external function.) This is part of the reason why compilers are prefered -- they take care of these details on a per case basis, so there's no thought/worrying required.
One way for the internal/external calls to go would be to have two entry-points to the same function -- one for the external calls where esi needs setting up, etc; and the other for the internal call where it's already set up -- the problem is that you'd probably need two exits too, which then requires you to know which entrance you came through.... complexity... not worth it.... etc.
So, unfortunately you're left with making sure that all of the externally available calls follow the 'standard' and you can at least optimise the 'private' functions. This is probably the best compromise.
Personally I don't spend too much effort of such things, an extra mov here and there isn't going to slow me down too much compared to the time spent trying to avoid it. And for 'object' type programming, I tend to go for an object/struct-pointer in ecx, and make sure that's set before calling the 'member' functions; even though I know that in many cases it will be put straight into ebx, even though it was probably already in ebx, but you have to do it just for the case that it MIGHT not be -- avoiding errors is more important than avoiding 'wasting' an extra instruction.
No snowflake in an avalanche feels responsible.

hutch--

You can reliably use EAX ECX & EDX to pass arguments to another procedure and use either STDCALL or C calling convention for any more which is close enough to what FASTCALL does and still remain within normal register preservation rules.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

gabor

Hello!

Thanks for the wise posts. I have to mention two things :

Quote from: Tedd on October 17, 2006, 09:19:15 PM
Personally I don't spend too much effort of such things, an extra mov here and there isn't going to slow me down too much compared to the time spent trying to avoid it.

1. That's right, but imagine that the method I want to use is called a million times. (A huge linked list updating procedure) Well in such scales an instruction here, and an instruction there can count. And execution time is a hot factor.
2. Putting the offset of a struct/obj in ecx looks good, but (and I may be wrong) I experienced that when using ecx as the cycle variable and esi as the index register can speed up the execution. Was this a random event or does it have some real reason?

Greets, Gábor