AMD64-only: Up to .NET Framework 4.5, the managed "this" pointer was treated just like the native "this" pointer (meaning it was the second argument when the call used a return buffer and was passed in RDX instead of RCX).
I found the above statement in the clr abi document, based on my understanding that it is up to .NET Framework 4.5, "this" pointer was the second argument when the call used a return buffer and was passed in RDX;
So I have two questions:
What's the meaning for the buffer? The buffer referring to stream buffer like httpwebresponse or anything else?
How about if we use the static class, I think in that case, there is no "this" pointer exists and the above statement specified to the instance object , right?
It is clear that Microsoft has been tinkering with their x64 abi. Exactly what they did and when is however drastically unclear, documentation is incomplete, scatter-shot and poorly dated. I personally consider the reverse-engineering work done by Agner Fog to be only truly reliable source of information. He however doesn't tackle managed code generation.
The claim that this was changed in 4.5 is rather implausible. Much more likely is that this was changed in RyuJIT, the new x64 jitter to replace the rather buggy and unmaintainable legacy x64 jitter. Originally released in preview as 4.5.3, perhaps explaining the 4.5 claim, but incremented to 4.6 later. A likely inspiration for this change is the .NETCore project, the Microsoft x64 abi difference with Unix code generators (GNU and LLVM) are fairly painful.
But unlike the C++ compiler code generator changes (modified in a VS2015 update according to Agner Fog, gack), this change is pretty unlikely to be breaking. A mismatch could only occur in pinvoke, the jitter never supported CallingConvention.ThisCall. Luckily your questions are easy to answer:
What's the meaning for the buffer?
This matters only to methods that return an "aggregate type". In other words, a value that does not fit in CPU registers anymore. In C# that is a struct
, with the further stipulation that it needs to be non-trivial (more than 2 members or a non-trivial field type).
The method caller must allocate space in its stack frame for the return value (the "buffer") and passes a pointer to that space. That pointer is passed as a hidden extra argument to the method. Traditionally the first argument, before the hidden this
argument. That made this
the second argument and was therefore passed through the RDX register instead of the RCX register. The change reverses the order, this
is always the first argument and passed through RCX, the return value pointer is second. The called method copies the return value into that buffer before it returns.
How about if we use the static class
Only relevant to a static method. There is then no extra hidden this
argument so it is simply omitted. Effectively no change from the way it was done before, the hidden return value pointer is automatically always the 1st argument and is thus passed through RCX.
If this matters to you then be sure to take advantage of the Debugger's Debug > Windows > Disassembly window. Run it on a small test program, it easily tells you which registers are used.