Assembly, EBP, ESP, R8-R15 and addressing

Started by
10 comments, last by Rockoon1 16 years, 6 months ago
Are there any usage restrictions on the EBP and ESP registers or can they be used exactly like the other registers? I kind of remember there being some restriction on them but haven't found anything. Same case with the x86-64 registers (R8-R15)? For addressing if it's possible to hard code a physical address would that be the best thing to do? For example..
MOV EAX, dword ptr[EBP + 10]
becomes...
MOV EAX, dword ptr[12345678]
Any benefit to keeping the address in the register? Readability isn't important as this will be compiled code and the physical addresses will be known at compile time. I also won't need to modify this address as the program runs. I'm guessing the second method will decode to a few less micro op's but performance difference should be negligible (potentially free register though). Thanks in advance
Advertisement
It’s been a while since my last line of assembly but let me try to answer this.
I think you can use EBP like other registers, and potentially you could use it to store any value. As a kind of convention in languages like C/C++, EBP holds the stack base pointer which is used to
1. build the function calling / return mechanism
2. allocate variables on the stack.

ESP holds the stack current pointer so I would be careful not to mess with it.

So if you are not in 100% assembly program, like inside a C/C++ program, you should be very careful to save EBP’s value before messing with it, by pushing it on the stack and popping it later on.
I found This useful when I was doing x64 assembly stuff.
Quote:Original post by bulgurmayo
ESP holds the stack current pointer so I would be careful not to mess with it.
Good point, forgot about that.

Quote:Original post by Evil Steve
I found This useful when I was doing x64 assembly stuff.
Thanks for the link.
The 16-bit BP register used to have restrictions (and benefits) on it

In the 32bit era and beyond, EBP/RBP is no longer so asymetric because CS = DS = ES = SS .. the old segment preferences and restrictions of some instructions/registers are no longer meaningful.

btw, a decent compiler (or asm programmer) NEVER uses a stack frame.. ever.. if you cant figure out where things are relative to ESP/RSP then you are probably already dead in the water
Quote:Original post by Rockoon1
Tbtw, a decent compiler (or asm programmer) NEVER uses a stack frame.. ever.. if you cant figure out where things are relative to ESP/RSP then you are probably already dead in the water


That's not entirely true. It's not difficult to have a situation where the data pushed onto the stack is indeterminate, i.e. some data is only pushed is certain conditions are met.

The only real issue with using BP for memory access is the use of SS by default.

Skizz
Quote:Original post by Rockoon1
btw, a decent compiler (or asm programmer) NEVER uses a stack frame.. ever.. if you cant figure out where things are relative to ESP/RSP then you are probably already dead in the water


GCC does for x86 IIRC, even with -O#, because it allows some minimal debugging and tracing. You have to explicitly pass -fomit-frame-pointer if you don't want them.

As for the OP's question, having EBP free probably wont get you much anyway, internally the processor has many more unnamed registers and will use them to side step write-after-read stalls, so you might be better off setting stack frames and making debugging easier if that is a requirement.

Edit:

Forgot about your other question re: hard coding the address. Which one is better depends on the context. The version with the entire address hard coded in the instruction will obviously be longer, and will probably decode into more micro-ops that break the instruction into 2 (or more) steps, loading the address from just after opcode, then loading the value from that address.

Used sparingly you can make life easier on yourself by using the hard coded one, but inside a loop you would be better off loading the address once outside the loop and using the other one.

[Edited by - outRider on October 10, 2007 11:51:40 AM]
Quote:Original post by Skizz
That's not entirely true.


its not?

Quote:Original post by Skizz
It's not difficult to have a situation where the data pushed onto the stack is indeterminate, i.e. some data is only pushed is certain conditions are met.


pushes? heh... a really good compiler wont be doing that either.. especially under win64 where the stack should always be aligned to 16 byte boundaries

Allocate enough local space, aligned to the requirements, for the worst case at the start of the procedure.

stop thinking like a black-box compiler :)

Quote:Original post by Skizz
The only real issue with using BP for memory access is the use of SS by default.


Thats not an issue. ss = ds under win32 and win64, and probably under linux as well.

Quote:Original post by outRider
GCC does for x86 IIRC, even with -O#, because it allows some minimal debugging and tracing. You have to explicitly pass -fomit-frame-pointer if you don't want them.


gcc is not a good compiler.

it is great for what it is .. an open source cross-platform compiler .. but it is not intended to produce the tightest, fastest code on x86(-64) .. icc on the other hand only targets x86(-64) and a stack frame would be a rare exception even without optimizations ..

it is extra work that is not necessary, which also arbitrarily ties up a register .. doesnt that sum it up?
Quote:Original post by Rockoon1
Quote:Original post by Skizz
That's not entirely true.


its not?

Quote:Original post by Skizz
It's not difficult to have a situation where the data pushed onto the stack is indeterminate, i.e. some data is only pushed is certain conditions are met.


pushes? heh... a really good compiler wont be doing that either.. especially under win64 where the stack should always be aligned to 16 byte boundaries

Allocate enough local space, aligned to the requirements, for the worst case at the start of the procedure.

stop thinking like a black-box compiler :)

You think too much like a Windows programmer. There are millions of IA32 systems out there that don't run Windows or *nix (VxWorks for example). My comments were really targeted to assembly programming and not compilers (I should have made that clear). Optimising compilers almost certainly allocate stack space for the worst case scenario. But this is not always a good thing to do, assembly code on limited resource systems would need to keep memory use to a minimum - unused memory is wasted after all, and so memory is only reserved if it is actually going to be used. In this case, SP relative addressing is not really an option. I agree that this scenario is not very common, but it can happen. As for the pushes issue, how about an assembly routine calling a C library function that takes va_args?

Quote:
Quote:Original post by Skizz
The only real issue with using BP for memory access is the use of SS by default.


Thats not an issue. ss = ds under win32 and win64, and probably under linux as well.


As I said above, what about all the other systems you could be programming for? Making assumptions could lead to errors in the future when the assumptions are no longer valid. If you assume that [BP] == [DI] for BP == DI then you'll get a nasty surprise one day.

Skizz

This topic is closed to new replies.

Advertisement