Link to home
Start Free TrialLog in
Avatar of zoltan082098
zoltan082098

asked on

Register use when calling Delphi procedure inside asm statement

Delphi help for register use inside asm...end statements says:
"An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers, but can freely modify the EAX, ECX, and EDX registers."
It's easy at the first blush, but what happens when there is another procedure/function call inside asm...end bracket, e.g. as following:

procedure tobject1.method1;

  procedure nestedfunction1(argument1: integer);
  begin
    ...
  end;

  procedure inlineassembly1;
  asm
    mov eax, something // argument1
    call nestedfunction1
  end;

begin
  ...
end;

Although I do not modify any registers but eax in inlineassembly1 procedure, I cannot be sure whether nestedfunction1 inside inlineassembly1 so does or not. In this case, in order to be sure about "preserving the EDI, ESI, ESP, EBP, and EBX registers" inlineassembly1 has to look like this:

  procedure inlineassembly1;
  asm
    pushad
    mov eax, something // argument1
    call nestedfunction1
    popad
  end;
 
Since assembly routines usually are time critical solutions, the above shovn one isn't a convenient solution at all.
Moreover, I cannot see such pushad...popad brackets when disassembling compiled delphi code in CPU window. Does it mean that I needn't care of register use of Delphi procedures called inside asm...end bracket?
Avatar of Wim ten Brink
Wim ten Brink
Flag of Netherlands image

I've checked... pushad and popad are visible in my own CPU window (Delphi 7) then I just add a piece of code like:

asm
  pushad
  popad
end;

And true, if something is very time-critical you might not want to call it through Assembler this way. You might want to implement this in standard Delphi code then. But in my experience asm won't give you that much increase in speed since Delphi compiles to reasonable fast binaries anyway. Only small, often-called functions do gain a performance speed through assembler...
Avatar of zoltan082098
zoltan082098

ASKER

I mean "I cannot see such pushad...popad brackets when disassembling compiled delphi code in CPU window" that Delphi itself never saves the registers by pushad before calling a procedure, that is, you won't see pushad/popad pairs bracketing a procedure in a compiled delphy binary UNLESS you place them in asm...end explicitly.

BTW please trust me regarding the speed increasing in my assembler routines, they are called about several million times during the data processing. I only want to avoid the following solutions:

  asm
    ... // some assembly code here
  end;
  nestedfunction1($1234)
  asm
    ... // continuing the assembly code
  end;

Since I can better use following:

  asm
    ...
    mov eax, $1234
    call nestedfunction1
    ....
  end;

The only problem regarding this solution is:
Do I have to take care about saving and restoring all the registers before and after calling nestedfunction1? If not, which are the registers to save in this situation?

Regards,
ZOltan
pushad/popad would be faster in my opinion than just pushing/popping specific registers.

You must make sure EDI, ESI, ESP, EBP, and EBX don't change after calling the procedure from your ASM code but I don't think a Delphi procedure will change them. This rule would apply to ALL procedures in Delphi, not just assembler procedures. Furthermore, you must preserve those registers that you need to remember for yourself. Thus if you store some pointer in EAX, you need to push/pop it before/after calling the procedure. All other registers are those you don't need and Delphi doesn't care about them.

If a register doesn't contain any valuable info then why store it? Whatever function is calling your assembler routine, it cannot assume all registers stay unchanged. Only EDI, ESI, ESP, EBP, and EBX must be kept. All others are not important. (Unless you're returning a value, of course. Then some registers are pointing to this data.)

Advise: Just try your code without pushing/popping anything... If the code breaks, you'll have to push some registers.
Delphi assumes that nobody is suppose to play with the registers, so it need not bother to push/pop it in every call.  Imagine the overhead it will cause if need to.
Thus exist the rule,
"An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers,..."

It means that if you are going to play with these registers, you must preserve it and pop it back upon return.  If you are sure you do not use them. Don't bother.  And you are free to play with the rest of the registers.

Now, looking back at your code,

    mov eax, something // argument1
    call nestedfunction1

following the same rule, nestedfucntion1 will not preserve EAX for you. so when it comes back, you cannot rely on its value also.
Guys,
it may be about my poor English but I suspect You still not really understand what's the problem here.
My code looks like this:

asm
   
    // section 1
    ...
    // end of section 1  
   
    // section 2
    mov eax, $1234
    call nestedfunction1
    // end of section 2

    // section 3
    ...
    // end of section 3
 
end;

"An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers,...", that is, the state of these registers must be the same BEFORE section1 and AFTER section3 respectively.

Let' say that both section1 and section3 use just eax, ecx and edx registers. Can I be sure that the EDI, ESI, ESP, EBP, and EBX registers has been preserved in this asm statement?

Of course not, because of the section2, which contains the call to nestedfunction1, and I have no clue whether a call to an ARBITRARY Delphi function/procedure applies the same rule or not, that is, to preserve the above mentioned registers.

So, there is the big question:

Do I have to save/restore these registers before/after calling an arbitrary Delphi procedure inside an asm statement or not (or, think about the worst case, wnen such Delphi procedures does contain calls to WIN32 API functions)? If so, please show me some guidelines how can I decide eaxctly WHICH registers have to be saved befor calling Delphi functions?

Regards,
Zoltan
ASKER CERTIFIED SOLUTION
Avatar of aruana
aruana
Flag of Malaysia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Aruana is correct. Delphi will preserve the same registers as the ones you're supposed to preserve. Delphi needs them internally. And actually, when you call nestedfunction1 those registers must contain correct values, the values they had when your routine was called. It will not mess up the OS, though, since the OS will just kick any bad-behaving application to the Bad-Application-Hell. (In other words: your application crashes.)

Thus, preserve those registers.
"It will not mess up the OS, though, since the OS will just kick any bad-behaving application to the Bad-Application-Hell."
Well Alex, no offence, only with Windows XP, maybe.  But I still would not bet my money on it.  :-)
No offence to Microsoft either.  But that is how it is.