Can an x64 function repurpose parameter home space as general scratch space?

We saw some time ago that the x64 calling convention in Windows reserves space for the register parameters on the stack, in case the called function wants to spill them. But can the called function use the memory for other purposes, too?

You sort of already know the answer to this question. Consider this function:

void testfunction(int a)
 a = 42;

How would a naïve compiler generate code for this function?

    sub rsp, 8 ;; realign the stack

    ;; spill all register parameters into home locations
    mov [rsp+0x10], rcx
    mov [rsp+0x18], rdx
    mov [rsp+0x20], r8
    mov [rsp+0x28], r9

    ;; a = 42
    mov [rsp+0x10], 42

    ;; return
    add rsp, 8 ;; clean up local frame

Observe that after spilling the register parameters into their home locations onto the stack, the function modified the local variable, which updated the value in the home location.

Since a function can arbitrarily modify a parameter, you can see that a function is therefore allowed to arbitrarily modify a parameter's home location. At which point you can see that an optimizing compiler might choose an arbitrary value completely unrelated to the parameter.

Our test function has only one parameter. What about the other three home registers?

The caller is responsible for allocating space for parameters to the callee, and must always allocate sufficient space for the 4 register parameters, even if the callee doesn't have that many parameters.

A function can therefore treat those 32 bytes as bonus free play. The rationale behind those 32 bytes is that it gives you a place to spill your inbound register parameters so that they will be adjacent to the stack-based parameters. (We saw how the naïve compiler took advantage of this by not trying to be clever in its function prologue and simply spilling all register parameters whether it needs them or not.)

Nevertheless, you are free to use them for whatever purpose you like, and if you're looking at heavily-optimized code, you'll probably find that the compiler found all sorts of clever things it can do with them. For example, a common trick is to use them to save the nonvolatile registers that the function locally uses to hold the corresponding parameter!

(Did this article look familiar? Turns out I covered this article a few years ago, but I'm senile and accidentally repeated a topic. And since I put so much effort into writing it, I'm going to make you suffer through it, even though it's a repeat. Hey, television programs repeat during the summer.)

Comments (23)
  1. alegr1 says:

    Don't worry Raymond. We're always happy to hijack your duplicate topics with very tangential rants.

  2. kinokijuf says:

    Please stop usïng gratuïtïous umlauts. Ït leads to ïrrelevant comments.

  3. parkrrrr says:

    Well, since they're neither gratuitous nor umlauts, I guess he's okay. We can eliminate irrelevant comments in our time! All we need is your coöperation.

  4. But an ï with two dots looks soooo cute!

  5. Random User 0238804 says:


    As parkrrr states, they are not umlauts. They are diaereses. Their purpose is to clarify that the indicated vowel does not participate in a digraph or diphthong.

    And they are not gratuitous. I actually know people who would pronounce "naïve" as expected, but would pronounce "naive" like "nave".

  6. "(Did this article look familiar? Turns out  I covered this article a few years ago, but I'm senile and accidentally repeated a topic. And since I put so much effort into writing it, I'm going to make you suffer through it, even though it's a repeat. Hey, television programs repeat during the summer.)"

    Don't worry, I also have a short memory. I didn't actually realise it was repeated until the final paragraph.

    But having a short memory is good, it makes life feel fresh and new again.

  7. alegr1 says:

    As a proverb goes, "new is just a well forgotten old".

  8. alegr1 says:

    diæreses is what you get after dining in fäšt foöd.

  9. JM says:

    Well as long as we're repeating things, you can look up…/10424279.aspx for more tangential discussion on those little dots.

  10. Well, it's a repeated article for who has been following the blog since the first appearance of that article.

    For who, like me, that sees for the first time the article and didn't go through the previous', it's the first time.

    So thank you Raymond for your effort in repeating articles.

  11. Ken in NH says:

    An old adage in pedagogy is to tell the audience, repeat yourself, then tell it to them again. You only need to re-post this article again in a few years to really make it stick in our memories.

  12. ulric says:

    are you guys talking about the word "naïve"?  that's a french word, written exactly like that with a "trema", and that spelling is noted as a variant in every english dictionnary.

  13. us says:

    I'm going with "naive", there's no reason to use smelly surrender letters when there's a fine native replacement.

  14. Veltas says:

    Won't this convention just make function calls heavier? And what was the point of allocating that 8 bytes of stack space that's never used?

  15. Veltas says:

    By heavier I'm talking about the sudden extra requirement of making 32 bytes of stack space extra with each function call, if I've understood the convention.

  16. alegr1 says:


    These 32 bytes are always there and don't have to be allocated/deallocated. It is allocated once when the ESP is adjusted for local variables.

  17. Gabe says:

    alegr1: I think Veltas is asking why the calling convention includes slots for 4 parameters regardless of how many the function has. In other words, shouldn't a 1-parameter function only require 1 spill slot? Or couldn't the callee determine how much space it needs on the stack and use it accordingly?

    [One word: open. Two more words: Unprototyped functions. -Raymond]
  18. Veltas says:

    Okay, "sub rsp, 8 ;; realign the stack".

    What's the point of that line? Is this just part of a convention that's not necessary but just nicer somehow?

    Also, I was interested as to the point of the 32 bytes of spill space by default, surely the callee could just allocate as required (yep Gabe, you had that right).

    Sorry if that was the answer in the last post, Raymond, but I didn't understand if that was so unfortunately.

    [Please read the linked article first. -Raymond]
  19. Veltas says:

    Okay I've read the article and now understand the line that realigns the stack.

    I see the reason for having the 32 bytes of space is in case you want to spill them, and you say that this is useful if the function is variadic, how so if you don't mind me asking?

    [Think about it. If you haven't figured it out, you can wait for my answer in two years. -Raymond]
  20. Falcon says:


    The code is simpler if all parameters can simply be retrieved from the stack, instead of worrying about which ones are in registers and which ones are in memory.

  21. John Doe says:

    So, the scratch space is a case of optimizing for the uncommon case.

    [The alternative is being broken in certain special cases. And it doesn't really cost you anything in the common case anyway. -Raymond]
  22. John Doe says:

    @Raymond, what is bad, and I recall, is the optimization of the uncommon case, but this time I'll add, at the cost of making everything else pay for it.

    For instance, let's take the AMD64 ABI, used by every other kid. It doesn't have a scratch space whose purpose is to easily allow spilling in varargs functions. Yet, it isn't broken in the special cases.

    Certainly, all compilers must be smart about the use of va_*, but the other kids' compilers must be way smarter. However, that's a one-off effort, so the justification that va_* is much simpler in this kid's single convention is just a distraction.

    It does cost you something in the rather common case: 32 bytes of caller-allocated stack for optional callee consumption. Some or all of it will end up unused. This kid doesn't know, it can't know, but it gives that space on each call, anyway.

    [I think you'll find that in practice, it all gets used. As noted in the article, it is typically used as spill space for preserved registers, space which you need to allocate anyway. -Raymond]

Comments are closed.

Skip to main content