{"id":112436,"date":"2026-06-17T07:00:00","date_gmt":"2026-06-17T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112436"},"modified":"2026-06-18T07:17:14","modified_gmt":"2026-06-18T14:17:14","slug":"20260617-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260617-00\/?p=112436","title":{"rendered":"Windows stack limit checking retrospective, follow-up"},"content":{"rendered":"<p><a href=\"https:\/\/aarongiles.com\/\"> Aaron Giles<\/a> worked on porting Windows to both ARM32 and AArch64, and he <a href=\"https:\/\/bsky.app\/profile\/did:plc:rozrlqzq7umwvv5etd7slmxz\/post\/3mhk2isogwc2q?ref_src=embed\"> noted a missing detail<\/a> in my <a title=\"Windows stack limit checking retrospective: arm64, also known as AArch64\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260320-00\/?p=112154\"> retrospective of stack limit checking on arm64<\/a>:<\/p>\n<p><style>.entry-content iframe { position: relative; }<\/style>\n<\/p>\n<blockquote class=\"bluesky-embed\" data-bluesky-uri=\"at:\/\/did:plc:rozrlqzq7umwvv5etd7slmxz\/app.bsky.feed.post\/3mhk2isogwc2q\" data-bluesky-cid=\"bafyreihi4tton7infgddx7csbqjm5xljewdwpfeoi27narlqdbfghlsp2a\" data-bluesky-embed-color-mode=\"system\">\n<p lang=\"en\">Every once in a while Raymond Chen does an architectural comparison series and I get to see (a paraphrased version of) some code I wrote way back when. He&#8217;s right about why we passed stack size\/16, but surprised he didn&#8217;t call out the unconventional x15 usage.<\/p>\n<p>\u2014 Aaron Giles (<a href=\"https:\/\/bsky.app\/profile\/did:plc:rozrlqzq7umwvv5etd7slmxz?ref_src=embed\">@aarongiles.com<\/a>) <a href=\"https:\/\/bsky.app\/profile\/did:plc:rozrlqzq7umwvv5etd7slmxz\/post\/3mhk2isogwc2q?ref_src=embed\"> Mar 20, 2026 at 8:08 PM<\/a><\/p><\/blockquote>\n<p><script async src=\"https:\/\/embed.bsky.app\/static\/embed.js\" charset=\"utf-8\"><\/script><\/p>\n<p>I&#8217;m guessing that by &#8220;unconventional x15 usage&#8221;, Aaron means &#8220;Why is the parameter passed in the <code>x15<\/code> register? The AArch64 calling convention passes the first parameter in the <code>x0<\/code> register, so shouldn&#8217;t that parameter be in the <code>x0<\/code> register?&#8221;<\/p>\n<p>It seemed so obvious to me that I didn&#8217;t consider it worth mentioning.<\/p>\n<p>The function that needs to do a stack probe is in a bit of a bind: It has inbound parameters, some of which might be passed in registers. If the stack size parameter were passed like a normal parameter to the stack probe function, then the calling function has to save its original inbound parameters somewhere. But it can&#8217;t save them on the stack because it has to do a stack probe before it can use the stack.<\/p>\n<p>The solution is to give the stack probe function a custom calling convention that limits itself to scratch registers that are not used for receiving inbound parameters.<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Architecture<\/th>\n<th>Used for<br \/>\nparameters<\/th>\n<th>Allocation<br \/>\nsize<\/th>\n<th>Also modified<\/th>\n<\/tr>\n<tr>\n<td>8086<\/td>\n<td>&nbsp;<\/td>\n<td><tt>ax<\/tt><\/td>\n<td><tt>bx<\/tt>, <tt>dx<\/tt><\/td>\n<\/tr>\n<tr>\n<td>x86-32<\/td>\n<td><tt>ecx<\/tt><\/td>\n<td><tt>eax<\/tt><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>MIPS<\/td>\n<td><tt>a0<\/tt>\u2026<tt>a3<\/tt><\/td>\n<td><tt>t8<\/tt><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>PowerPC<\/td>\n<td><tt>r3<\/tt>\u2026<tt>r10<\/tt><\/td>\n<td><tt>r12<\/tt><\/td>\n<td><tt>r0<\/tt>, <tt>r11<\/tt><\/td>\n<\/tr>\n<tr>\n<td>Alpha AXP<\/td>\n<td><tt>a0<\/tt>\u2026<tt>a5<\/tt><\/td>\n<td><tt>t12<\/tt><\/td>\n<td><tt>t8<\/tt>, <tt>t9<\/tt>, <tt>t10<\/tt><\/td>\n<\/tr>\n<tr>\n<td>x86-64<\/td>\n<td><tt>rcx<\/tt>, <tt>rdx<\/tt>, <tt>r8<\/tt>, <tt>r9<\/tt><\/td>\n<td><tt>rax<\/tt><\/td>\n<td><tt>r10<\/tt>, <tt>r11<\/tt><\/td>\n<\/tr>\n<tr>\n<td>AArch64<\/td>\n<td><tt>x0<\/tt>\u2026<tt>x7<\/tt><\/td>\n<td><tt>x15<\/tt><\/td>\n<td><tt>x16<\/tt>, <tt>x17<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The calling conventions for processor architectures designate certain registers as &#8220;super-volatile&#8221;, typically those used reserved for assembler temporaries or for facilitating function calls between modules. These registers are excellent candidates for use by the stack probe function since there is no way they could be used for normal parameter passing.<\/p>\n<p>For example, PowerPC uses <tt>r11<\/tt>, and AArch64 uses <tt>r16<\/tt> and <tt>r17<\/tt>, all of which are available for use in function glue stubs. Other opportunities were overlooked: MIPS and Alpha AXP could have used <tt>at<\/tt>, though I can see why they may have wanted to avoid using them because the assembler might use them implicitly when assembling pseudo-instructions.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Choosing the register to use to pass the desired stack allocation size.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-112436","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Choosing the register to use to pass the desired stack allocation size.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112436"}],"version-history":[{"count":2,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112436\/revisions"}],"predecessor-version":[{"id":112442,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112436\/revisions\/112442"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}