1

According to wikipedia: "Microsoft or GCC __fastcall convention (aka __msfastcall) passes the first two arguments (evaluated left to right) that fit into ECX and EDX. Remaining arguments are pushed onto the stack from right to left."

Why did they decide against using EAX, ECX, EDX for arg0, arg1, arg2? If they're going to push arguments into the registers, why stop at 2? I know Borland's fast all DOES do this, so did microsoft choose not to use EAX just to be different?

Yifan
  • 4,867
  • 5
  • 25
  • 24
  • Do you want to know their actual reason or just some plausible explanation? – harold Feb 04 '14 at 18:24
  • I would prefer the actual reason. I understand there could be reasons like "majority of ms functions use two or less arguments" or "EAX will always store return value so if the parameter needs to be used, it would have to be saved". However, ARM made the design choice to use R0 as first parameter. It would be nice to see why MS decided on two passed in registers. – Yifan Feb 04 '14 at 18:28
  • 4
    This is ancient history, goes back when these compilers had very different goals. Borland compilers compiled fast, GCC and Microsoft focused more on their code optimizers. Getting EAX to pay off is pretty difficult, it is used in far too many instructions. You need a backing store to preserve it and that better be cheap or the code isn't faster at all. Borland historically did not have a good optimizer that knew how to use registers efficiently. So no lack for a backing store either. – Hans Passant Feb 04 '14 at 19:26

1 Answers1

-3

Due to limitations of x86 commands set, there is no
CALL immediate
command, there is
CALL IP.offset,
which is relative. Of course, compiler writers would like to reserve something with absolute offset, and there could be a request from processor makers to do so, hence we have the following "compromise":
MOV eax, absolute_address(label)
CALL eax
which would be equal to
CALL absolute_address(label)
Such calling method would require 1 temporary register, just for call, which can be easily reused, and EAX is the best option for such purpose.
The result of such considerations is useful, and you can use it in your asm code. The benchmarks show that the branch predictors would, at least partially, work properly with such code. Another possibility, but a rare one, is when you have to reset flags to avoid dependency penalty or partial register stall when calling procedure which is already in top level cache. It would happen, for example, if you use something like mov ah, 1 at the very beginning of your subroutine. To avoid that, use EAX as a temporary register and put
XOR eax, eax
immediately before CALL. It may actually save a few clock cycles in some rare cases. But the actual benefit of doing that vs. using it for parameter passing is doubtful and the reasons for that may be as stated above.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Programmer
  • 29
  • 2
  • 1
    This reads more like a rant than a professional/technical answer - please delete all the unnecessary verbiage and reduce this to just the relevant technical details. – Paul R Oct 18 '14 at 09:43
  • For now I would like to see how long something "politically incorrect" may last, and post the screens when the reactions become extreme then, then improve and possibly post as evidence elsewhere. As for particular reasons what is not right (not "politically" though), any feedback is welcomed. – Programmer Oct 18 '14 at 10:07