Why does IA-32 have a non-intuitive caller and callee register saving convention?

不羁岁月 提交于 2019-11-28 00:28:41

Why would you want to write code to save registers in every function that you might not need? That would add extra code and extra memory writes to every single function call. It may not seem significant now, but back in the 80's when this convention was created it probably did matter.

And note that ia-32 doesn't have a fixed calling convention - what you list is only an external convention - ia-32 doesn't enforce it. If you're writing your own code you use the registers however you wish.

Also see the discussion History of Calling Conventions at the Old New Thing Blog.

When deciding which registers should be preserved by a calling convention, you need to balance the needs of the caller against the needs of the callee. The caller would prefer that all registers be preserved, since that removes the need for the caller to worry about saving/restoring the value across a call. The callee would prefer that no registers be preserved, since that removes the need to save the value on entry and restore it on exit.

If you require too few registers to be preserved, then callers become filled with register save/restore code. But if you require too many registers to be preserved, then callees become obligated to save and restore registers that the caller might not have really cared about. This is particularly important for leaf functions (functions that do not call any other functions).

A guess:

If the caller saves all registers it will still need after a function call, it wastes time when the called function doesn't modify all those registers.

If the callee saves all registers it changes, it wastes time when the caller didn't need the values in those registers again.

When some registers are saved by caller and some by callee, the compiler (or assembly programmer) can choose which kind to use depending on if the value is needed after the next function call.

If you look a little deeper into the registers used, you can see why they won't be preserved by the callee:

  • EAX: used for function returns, so quite obviously it cannot be preserved.
  • EDX:EAX: used for 64bit function returns, same as EAX.
  • ECX: this is the count register, and back in the older days of x86 when LOOPcc was 'cool', this register would be thrashed like mad, even today there are still quite a few instructions using ECX as a counter (like REP prefixed instructions). However, thanks to the advent of __thiscall and __fastcall, it gets used to pass args in, which means its very likely to change, so there is almost not point in preserving it.
  • ESP: this is a little side exception, as its not really preserved, its altered in accordance with stack changes. Though it can be preserved to prevent stack pointer corruption/security or unbalancing thanks to inline assembly (via stack frames).

Now it actually becomes intuitive :)

In short, caller save are because of argument passing. Everything else is callee save.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!