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?
"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:
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?
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
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.
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.
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.
ASKER
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
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.
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.
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...